Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> dependencies. May be * null if we don't care about module dependencies. * @param canModifyExterns If true, then we can move prototype * properties that are declared in the externs file. * @param anchorUnusedVars If true, then we must mark all vars as referenced, * even if they are never used. */ AnalyzePrototypeProperties(AbstractCompiler compiler, JSModuleGraph moduleGraph, boolean canModifyExterns, boolean anchorUnusedVars) { this.compiler = compiler; this.moduleGraph = moduleGraph; this.canModifyExterns = canModifyExterns; this.anchorUnusedVars = anchorUnusedVars; if (moduleGraph != null) { firstModule = moduleGraph.getRootModule(); } else { firstModule = null; } globalNode.markReference(null); externNode.markReference(null); symbolGraph.createNode(globalNode); symbolGraph.createNode(externNode); for (String property : IMPLICITLY_USED_PROPERTIES) { NameInfo nameInfo = getNameInfoForName(property, PROPERTY); if (moduleGraph == null) { symbolGraph.connect(externNode, null, nameInfo); } else { for (JSModule module : moduleGraph.getAllModules()) { symbolGraph.connect(externNode, module, nameInfo); } } } } @Override public void process(Node externRoot, Node root) { if (!canModifyExterns) { NodeTraversal.traverse(compiler, externRoot, new ProcessExternProperties()); } NodeTraversal.traverse(compiler, root, new ProcessProperties()); FixedPointGraphTraversal<NameInfo, JSModule> t = FixedPointGraphTraversal.newTraversal(new PropagateReferences()); t.computeFixedPoint(symbolGraph, Sets.newHashSet(externNode, globalNode)); } /** * Returns information on all prototype properties. */ public Collection<NameInfo> getAllNameInfo() { List<NameInfo> result = Lists.newArrayList(propertyNameInfo.values()); result.addAll(varNameInfo.values()); return result; } /** * Gets the name info for the property or variable of a given name, * and creates a new one if necessary

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> JSCompiler_stubMap[JSCompiler_stubMethod_id].apply(" + " this, arguments);" + " };" + "}" + "function JSCompiler_unstubMethod(" + " JSCompiler_unstubMethod_id, JSCompiler_unstubMethod_body) {" + " return JSCompiler_stubMap[JSCompiler_unstubMethod_id] = " + " JSCompiler_unstubMethod_body;" + "}"; /** * Creates a new pass for moving prototype properties. * @param compiler The compiler. * @param idGenerator An id generator for method stubs. * @param canModifyExterns If true, then we can move prototype * properties that are declared in the externs file. */ CrossModuleMethodMotion(AbstractCompiler compiler, IdGenerator idGenerator, boolean canModifyExterns) { this.compiler = compiler; this.idGenerator = idGenerator; this.moduleGraph = compiler.getModuleGraph(); this.analyzer = new AnalyzePrototypeProperties(compiler, moduleGraph, canModifyExterns, false); } @Override public void process(Node externRoot, Node root) { // If there are < 2 modules, then we will never move anything, // so we're done. if (moduleGraph != null && moduleGraph.getModuleCount() > 1) { analyzer.process(externRoot, root); moveMethods(analyzer.getAllNameInfo()); } } /** * Move methods deeper in the module graph when possible. */ private void moveMethods(Collection<NameInfo> allNameInfo) { boolean hasStubDeclaration = idGenerator.hasGeneratedAnyIds(); for (NameInfo nameInfo : allNameInfo) { if (!nameInfo.isReferenced()) { // The code below can't do anything with unreferenced name // infos. They should be skipped to avoid NPE since their // deepestCommonModuleRef is null. continue; } if (nameInfo.readsClosureVariables()) { continue; } JSModule deepestCommonModuleRef = nameInfo.getDeepestCommonModuleRef(); if (deepestCommonModuleRef == null) { compiler.report(JSError.make(NULL

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>/* * Copyright 2004 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.*; /** * Verifies that constants are only assigned a value once. * e.g. var XX = 5; * XX = 3; // error! * XX++; // error! * */ class ConstCheck extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType CONST_REASSIGNED_VALUE_ERROR = DiagnosticType.error( "JSC_CONSTANT_REASSIGNED_VALUE_ERROR", "constant {0} assigned a value more than once"); private final AbstractCompiler compiler; private final Set<Scope.Var> initializedConstants; /** * Creates an instance. */ public ConstCheck(AbstractCompiler compiler) { this.compiler = compiler; this.initializedConstants = new HashSet<Scope.Var>(); } @Override public void process(Node externs, Node root) { Preconditions.checkState(compiler.getLifeCycleStage().isNormalized()); NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: if (parent != null && parent.isVar() && n.hasChildren()) { String name = n.getString(); Scope.Var var =

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>. * */ public class TokenStream { public static boolean isKeyword(String name) { boolean id = false; String s = name; complete: { String X = null; int c; partial: switch (s.length()) { case 2: c=s.charAt(1); if (c=='f') { if (s.charAt(0)=='i') {id=true; break complete;} } else if (c=='n') { if (s.charAt(0)=='i') {id=true; break complete;} } else if (c=='o') { if (s.charAt(0)=='d') {id=true; break complete;} } break partial; case 3: switch (s.charAt(0)) { case 'f': if (s.charAt(2)=='r' && s.charAt(1)=='o') { id=true; break complete; } break partial; case 'i': if (s.charAt(2)=='t' && s.charAt(1)=='n') { id=true; break complete; } break partial; case 'n': if (s.charAt(2)=='w' && s.charAt(1)=='e') { id=true; break complete; } break partial; case 't': if (s.charAt(2)=='y' && s.charAt(1)=='r') { id=true; break complete; } break partial; case 'v': if (s.charAt(2)=='r' && s.charAt(1)=='a') { id=true; break complete; } break partial; } break partial; case 4: switch (s.charAt(0)) { case 'b': X="byte";id=true; break partial; case 'c': c=s.charAt(3); if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='a') { id=true; break complete;} } else if (c=='r') { if (s.charAt(2)=='a' && s.charAt(1)=='h') { id=true; break

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> complete; } } break partial; case 'e': c=s.charAt(3); if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='l') { id=true; break complete;} } else if (c=='m') { if (s.charAt(2)=='u' && s.charAt(1)=='n') { id=true; break complete;} } break partial; case 'g': X="goto";id=true; break partial; case 'l': X="long";id=true; break partial; case 'n': X="null";id=true; break partial; case 't': c=s.charAt(3); if (c=='e') { if (s.charAt(2)=='u' && s.charAt(1)=='r') { id=true; break complete;} } else if (c=='s') { if (s.charAt(2)=='i' && s.charAt(1)=='h') { id=true; break complete;} } break partial; case 'v': X="void";id=true; break partial; case 'w': X="with";id=true; break partial; } break partial; case 5: switch (s.charAt(2)) { case 'a': X="class";id=true; break partial; case 'e': X="break";id=true; break partial; case 'i': X="while";id=true; break partial; case 'l': X="false";id=true; break partial; case 'n': c=s.charAt(0); if (c=='c') { X="const";id=true; } else if (c=='f') { X="final";id=true; } break partial; case 'o': c=s.charAt(0); if (c=='f') { X="float";id=true; } else if (c=='s') { X="short";id=true; } break partial; case 'p': X="super";id=true; break partial; case 'r': X="throw";id=true; break partial; case 't': X

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>="catch";id=true; break partial; } break partial; case 6: switch (s.charAt(1)) { case 'a': X="native";id=true; break partial; case 'e': c=s.charAt(0); if (c=='d') { X="delete";id=true; } else if (c=='r') { X="return";id=true; } break partial; case 'h': X="throws";id=true; break partial; case 'm': X="import";id=true; break partial; case 'o': X="double";id=true; break partial; case 't': X="static";id=true; break partial; case 'u': X="public";id=true; break partial; case 'w': X="switch";id=true; break partial; case 'x': X="export";id=true; break partial; case 'y': X="typeof";id=true; break partial; } break partial; case 7: switch (s.charAt(1)) { case 'a': X="package";id=true; break partial; case 'e': X="default";id=true; break partial; case 'i': X="finally";id=true; break partial; case 'o': X="boolean";id=true; break partial; case 'r': X="private";id=true; break partial; case 'x': X="extends";id=true; break partial; } break partial; case 8: switch (s.charAt(0)) { case 'a': X="abstract";id=true; break partial; case 'c': X="continue";id=true; break partial; case 'd': X="debugger";id=true; break partial; case 'f': X="function";id=true; break partial; case 'v': X="volatile";id=true; break partial; } break partial; case 9: c=s.charAt(0); if (c=='i') { X="interface";id=true; } else if (c=='p') { X="protected";id=true; } else if (c=='t') { X="transient";id=true;

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> } break partial; case 10: c=s.charAt(1); if (c=='m') { X="implements";id=true; } else if (c=='n') { X="instanceof";id=true; } break partial; case 12: X="synchronized";id=true; break partial; } // patial match validate the entire string the one possiblity if (X!=null && X!=s && !X.equals(s)) return false; } return id; } public static boolean isJSIdentifier(String s) { int length = s.length(); if (length == 0 || !Character.isJavaIdentifierStart(s.charAt(0))) return false; for (int i=1; i<length; i++) { char c = s.charAt(i); if (!Character.isJavaIdentifierPart(c)) { if (c == '\\') { if (! ((i + 5) < length) && (s.charAt(i + 1) == 'u') && 0 <= xDigitToInt(s.charAt(i + 2), 0) && 0 <= xDigitToInt(s.charAt(i + 3), 0) && 0 <= xDigitToInt(s.charAt(i + 4), 0) && 0 <= xDigitToInt(s.charAt(i + 5), 0)) { return true; } } return false; } } return true; } /** * If character <tt>c</tt> is a hexadecimal digit, return * <tt>accumulator</tt> * 16 plus corresponding * number. Otherise return -1. */ private static int xDigitToInt(int c, int accumulator) { check: { // Use 0..9 < A..Z < a..z if (c <= '9') { c -= '0'; if (0 <= c) { break check; } } else if (c <= 'F') { if ('A' <= c) { c -= ('A' - 10); break check; } } else if (c <= 'f') { if ('a' <=

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> for few warnings. // This is probably the right trade-off, but will be slow if there // are lots of warnings in one file. js = getCode(); } catch (IOException e) { return null; } int pos = 0; int startLine = 1; // If we've saved a previous offset and it's for a line less than the // one we're searching for, then start at that point. if (lineNumber >= lastLine) { pos = lastOffset; startLine = lastLine; } for (int n = startLine; n < lineNumber; n++) { int nextpos = js.indexOf('\n', pos); if (nextpos == -1) { return null; } pos = nextpos + 1; } // Remember this offset for the next search we do. lastOffset = pos; lastLine = lineNumber; if (js.indexOf('\n', pos) == -1) { // If next new line cannot be found, there are two cases // 1. pos already reaches the end of file, then null should be returned // 2. otherwise, return the contents between pos and the end of file. if (pos >= js.length()) { return null; } else { return js.substring(pos, js.length()); } } else { return js.substring(pos, js.indexOf('\n', pos)); } } /** * Get a region around the indicated line number. The exact definition of a * region is implementation specific, but it must contain the line indicated * by the line number. A region must not start or end by a carriage return. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Returns {@code null} if it does not exist, * or if there was an IO exception. */ public Region getRegion(int lineNumber) { String js = ""; try { js = getCode(); } catch (IOException e) { return null; } int pos = 0; int startLine = Math.max(1, lineNumber - (SOURCE_EXCERPT_REGION_LENGTH + 1) / 2 + 1

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>); for (int n = 1; n < startLine; n++) { int nextpos = js.indexOf('\n', pos); if (nextpos == -1) { break; } pos = nextpos + 1; } int end = pos; int endLine = startLine; for (int n = 0; n < SOURCE_EXCERPT_REGION_LENGTH; n++, endLine++) { end = js.indexOf('\n', end); if (end == -1) { break; } end++; } if (lineNumber >= endLine) { return null; } if (end == -1) { int last = js.length() - 1; if (js.charAt(last) == '\n') { return new SimpleRegion(startLine, endLine, js.substring(pos, last)); } else { return new SimpleRegion(startLine, endLine, js.substring(pos)); } } else { return new SimpleRegion(startLine, endLine, js.substring(pos, end)); } } @Override public String toString() { return fileName; } public static SourceFile fromFile(String fileName, Charset c) { return builder().withCharset(c).buildFromFile(fileName); } public static SourceFile fromFile(String fileName) { return builder().buildFromFile(fileName); } public static SourceFile fromFile(File file, Charset c) { return builder().withCharset(c).buildFromFile(file); } public static SourceFile fromFile(File file) { return builder().buildFromFile(file); } public static SourceFile fromCode(String fileName, String code) { return builder().buildFromCode(fileName, code); } public static SourceFile fromCode(String fileName, String originalPath, String code) { return builder().withOriginalPath(originalPath) .buildFromCode(fileName, code); } public static SourceFile fromInputStream(String fileName, InputStream s) throws IOException { return builder().buildFromInputStream(fileName, s); } public static SourceFile fromInputStream(String fileName, String originalPath, InputStream s) throws IOException { return builder().withOriginalPath(

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>originalPath) .buildFromInputStream(fileName, s); } public static SourceFile fromReader(String fileName, Reader r) throws IOException { return builder().buildFromReader(fileName, r); } public static SourceFile fromGenerator(String fileName, Generator generator) { return builder().buildFromGenerator(fileName, generator); } /** Create a new builder for source files. */ public static Builder builder() { return new Builder(); } /** * A builder interface for source files. * * Allows users to customize the Charset, and the original path of * the source file (if it differs from the path on disk). */ public static class Builder { private Charset charset = Charsets.UTF_8; private String originalPath = null; public Builder() {} /** Set the charset to use when reading from an input stream or file. */ public Builder withCharset(Charset charset) { this.charset = charset; return this; } /** Set the original path to use. */ public Builder withOriginalPath(String originalPath) { this.originalPath = originalPath; return this; } public SourceFile buildFromFile(String fileName) { return buildFromFile(new File(fileName)); } public SourceFile buildFromFile(File file) { return new OnDisk(file, originalPath, charset); } public SourceFile buildFromCode(String fileName, String code) { return new Preloaded(fileName, originalPath, code); } public SourceFile buildFromInputStream(String fileName, InputStream s) throws IOException { return buildFromCode(fileName, CharStreams.toString(new InputStreamReader(s, charset))); } public SourceFile buildFromReader(String fileName, Reader r) throws IOException { return buildFromCode(fileName, CharStreams.toString(r)); } public SourceFile buildFromGenerator(String fileName, Generator generator) { return new Generated(fileName, originalPath, generator); } } ////////////////////////////////////////////////////////////////////////////// // Implementations /** * A source file where the code has been preloaded. */ static class Preloaded extends SourceFile { private static final long serialVersionUID = 1L; Preloaded(String fileName, String originalPath, String code) { super(fileName); super.set

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> { preferredBreakPosition = len; } } maybeCutLine(); } /** * This may start a new line if the current line is longer than the line * length threshold. */ @Override void maybeCutLine() { if (lineLength > lineLengthThreshold) { // Use the preferred position provided it will break the line. if (preferredBreakPosition > lineStartPosition && preferredBreakPosition < lineStartPosition + lineLength) { int position = preferredBreakPosition; code.insert(position, '\n'); reportLineCut(lineIndex, position - lineStartPosition); lineIndex++; lineLength -= (position - lineStartPosition); lineStartPosition = position + 1; } else { startNewLine(); } } } @Override void notePreferredLineBreak() { preferredBreakPosition = code.length(); } } static class Builder { private final Node root; private boolean prettyPrint = false; private boolean lineBreak = false; private boolean outputTypes = false; private int lineLengthThreshold = DEFAULT_LINE_LENGTH_THRESHOLD; private SourceMap sourceMap = null; private SourceMap.DetailLevel sourceMapDetailLevel = SourceMap.DetailLevel.ALL; // Specify a charset to use when outputting source code. If null, // then just output ASCII. private Charset outputCharset = null; private boolean tagAsStrict; /** * Sets the root node from which to generate the source code. * @param node The root node. */ Builder(Node node) { root = node; } /** * Sets whether pretty printing should be used. * @param prettyPrint If true, pretty printing will be used. */ Builder setPrettyPrint(boolean prettyPrint) { this.prettyPrint = prettyPrint; return this; } /** * Sets whether line breaking should be done automatically. * @param lineBreak If true, line breaking is done automatically. */ Builder setLineBreak(boolean lineBreak) { this.lineBreak = lineBreak; return this; } /** * Sets whether to output closure-style type annotations. * @param outputTypes If true, outputs closure-style type annotations. */ Builder setOutputTypes(boolean output

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>Types) { this.outputTypes = outputTypes; return this; } /** * Sets the line length threshold that will be used to determine * when to break lines, if line breaking is on. * * @param threshold The line length threshold. */ Builder setLineLengthThreshold(int threshold) { this.lineLengthThreshold = threshold; return this; } /** * Sets the source map to which to write the metadata about * the generated source code. * * @param sourceMap The source map. */ Builder setSourceMap(SourceMap sourceMap) { this.sourceMap = sourceMap; return this; } /** * @param level The detail level to use. */ Builder setSourceMapDetailLevel(SourceMap.DetailLevel level) { Preconditions.checkState(level != null); this.sourceMapDetailLevel = level; return this; } /** * Set the charset to use when determining what characters need to be * escaped in the output. */ Builder setOutputCharset(Charset outCharset) { this.outputCharset = outCharset; return this; } /** * Set whether the output should be tags as ECMASCRIPT 5 Strict. */ Builder setTagAsStrict(boolean tagAsStrict) { this.tagAsStrict = tagAsStrict; return this; } /** * Generates the source code and returns it. */ String build() { if (root == null) { throw new IllegalStateException( "Cannot build without root node being specified"); } Format outputFormat = outputTypes ? Format.TYPED : prettyPrint ? Format.PRETTY : Format.COMPACT; return toSource(root, outputFormat, lineBreak, lineLengthThreshold, sourceMap, sourceMapDetailLevel, outputCharset, tagAsStrict); } } enum Format { COMPACT, PRETTY, TYPED } /** * Converts a tree to js code */ private static String toSource(Node root, Format outputFormat, boolean lineBreak, int lineLengthThreshold, SourceMap sourceMap, SourceMap.DetailLevel sourceMapDetailLevel, Charset outputCharset, boolean tagAsStrict) { Preconditions.checkState

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>(sourceMapDetailLevel != null); boolean createSourceMap = (sourceMap != null); MappedCodePrinter mcp = outputFormat == Format.COMPACT ? new CompactCodePrinter( lineBreak, lineLengthThreshold, createSourceMap, sourceMapDetailLevel) : new PrettyCodePrinter( lineLengthThreshold, createSourceMap, sourceMapDetailLevel); CodeGenerator cg = outputFormat == Format.TYPED ? new TypedCodeGenerator(mcp, outputCharset) : new CodeGenerator(mcp, outputCharset); if (tagAsStrict) { cg.tagAsStrict(); } cg.add(root); mcp.endFile(); String code = mcp.getCode(); if (createSourceMap) { mcp.generateSourceMap(sourceMap); } return code; } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>COMMA) { Node gramps = parent.getParent(); if (gramps.isCall() && parent == gramps.getFirstChild()) { // Semantically, a direct call to eval is different from an indirect // call to an eval. See Ecma-262 S15.1.2.1. So it's ok for the first // expression to a comma to be a no-op if it's used to indirect // an eval. if (n == parent.getFirstChild() && parent.getChildCount() == 2 && n.getNext().isName() && "eval".equals(n.getNext().getString())) { return; } } if (n == parent.getLastChild()) { for (Node an : parent.getAncestors()) { int ancestorType = an.getType(); if (ancestorType == Token.COMMA) continue; if (ancestorType != Token.EXPR_RESULT && ancestorType != Token.BLOCK) return; else break; } } } else if (pt != Token.EXPR_RESULT && pt != Token.BLOCK) { if (pt == Token.FOR && parent.getChildCount() == 4 && (n == parent.getFirstChild() || n == parent.getFirstChild().getNext().getNext())) { // Fall through and look for warnings for the 1st and 3rd child // of a for. } else { return; // it might be ok to not have a side-effect } } boolean isSimpleOp = NodeUtil.isSimpleOperatorType(n.getType()); if (isSimpleOp || !NodeUtil.mayHaveSideEffects(n, t.getCompiler())) { if (n.isQualifiedName() && n.getJSDocInfo() != null) { // This no-op statement was there so that JSDoc information could // be attached to the name. This check should not complain about it. return; } else if (NodeUtil.isExpressionNode(n)) { // we already reported the problem when we visited the child. return; } String msg = "This code lacks side-effects. Is there a bug?"; if (n.isString()) { msg = "Is there a missing

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> the $ must be * upper case. * $A Constant - doesn't have to be anything in front of the $ * </pre> */ @Override public boolean isConstant(String name) { if (name.length() <= 1) { return false; } // In compiled code, '$' is often a namespace delimiter. To allow inlining // of namespaced constants, we strip off any namespaces here. int pos = name.lastIndexOf('$'); if (pos >= 0) { name = name.substring(pos + 1); if (name.length() == 0) { return false; } } return isConstantKey(name); } @Override public boolean isConstantKey(String name) { if (name.isEmpty() || !Character.isUpperCase(name.charAt(0))) { return false; } // hack way of checking that there aren't any lower-case letters return name.toUpperCase().equals(name); } /** * {@inheritDoc} * * <p>This enforces Google's convention about enum key names. They must match * the regular expression {@code [A-Z0-9][A-Z0-9_]*}. * * <p>Examples: * <ul> * <li>A</li> * <li>213</li> * <li>FOO_BAR</li> * </ul> */ @Override public boolean isValidEnumKey(String key) { return ENUM_KEY_PATTERN.matcher(key).matches(); } /** * {@inheritDoc} * * <p>In Google code, parameter names beginning with {@code opt_} are * treated as optional arguments. */ @Override public boolean isOptionalParameter(Node parameter) { return parameter.getString().startsWith(OPTIONAL_ARG_PREFIX); } @Override public boolean isVarArgsParameter(Node parameter) { return VAR_ARGS_NAME.equals(parameter.getString()); } /** * {@inheritDoc} * * <p>In Google code, any global name starting with an underscore is * considered exported. */ @Override public boolean isExported(String name, boolean local) {

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> return super.isExported(name, local) || (!local && name.startsWith("_")); } /** * {@inheritDoc} * * <p>In Google code, private names end with an underscore, and exported * names are never considered private (see {@link #isExported}). */ @Override public boolean isPrivate(String name) { return name.endsWith("_") && !isExported(name); } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> LinkedDirectedGraphNode(N nodeValue) { this.value = nodeValue; } @Override public N getValue() { return value; } @Override public <A extends Annotation> A getAnnotation() { throw new UnsupportedOperationException( "Graph initialized with node annotations turned off"); } @Override public void setAnnotation(Annotation data) { throw new UnsupportedOperationException( "Graph initialized with node annotations turned off"); } @Override public String getColor() { return "white"; } @Override public String getId() { return "LDN" + hashCode(); } @Override public String getLabel() { return value != null ? value.toString() : "null"; } @Override public String toString() { return getLabel(); } @Override public List<DiGraphEdge<N, E>> getInEdges() { return inEdgeList; } @Override public List<DiGraphEdge<N, E>> getOutEdges() { return outEdgeList; } private Iterator<GraphNode<N, E>> neighborIterator() { return new NeighborIterator(); } private class NeighborIterator implements Iterator<GraphNode<N, E>> { private final Iterator<DiGraphEdge<N, E>> in = inEdgeList.iterator(); private final Iterator<DiGraphEdge<N, E>> out = outEdgeList.iterator(); @Override public boolean hasNext() { return in.hasNext() || out.hasNext(); } @Override public GraphNode<N, E> next() { boolean isOut = !in.hasNext(); Iterator<DiGraphEdge<N, E>> curIterator = isOut ? out : in; DiGraphEdge<N, E> s = curIterator.next(); return isOut ? s.getDestination() : s.getSource(); } @Override public void remove() { throw new UnsupportedOperationException("Remove not supported."); } } } /** * A directed graph node with annotations. */ static class AnnotatedLinkedDirectedGraphNode<N, E> extends LinkedDirectedGraphNode<N, E> { protected Annotation annotation; /** * @param nodeValue Node's value. */ AnnotatedLinkedDirected

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>ASCRIPT3); if (config.isIdeMode || config.languageMode != LanguageMode.ECMASCRIPT3) { // Do our own identifier check for ECMASCRIPT 5 compilerEnv.setReservedKeywordAsIdentifier(true); compilerEnv.setAllowKeywordAsObjectPropertyName(true); } if (config.isIdeMode) { compilerEnv.setAllowMemberExprAsFunctionName(true); } compilerEnv.setIdeMode(config.isIdeMode); Parser p = new Parser(compilerEnv, errorReporter); AstRoot astRoot = null; try { astRoot = p.parse(sourceString, sourceFile.getName(), 1); } catch (EvaluatorException e) { logger.info( "Error parsing " + sourceFile.getName() + ": " + e.getMessage()); } finally { Context.exit(); } Node root = null; if (astRoot != null) { root = IRFactory.transformTree( astRoot, sourceFile, sourceString, config, errorReporter); root.setIsSyntheticBlock(true); } return root; } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSTypeExpression; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Prepare the AST before we do any checks or optimizations on it. * * This pass must run. It should bring the AST into a consistent state, * and add annotations where necessary. It should not make any transformations * on the tree that would lose source information, since we need that source * information for checks. * * @author johnlenz@google.com (John Lenz) */ class PrepareAst implements CompilerPass { private final AbstractCompiler compiler; private final boolean checkOnly; PrepareAst(AbstractCompiler compiler) { this(compiler, false); } PrepareAst(AbstractCompiler compiler, boolean checkOnly) { this.compiler = compiler; this.checkOnly = checkOnly; } private void reportChange() { if (checkOnly) { Preconditions.checkState(false, "normalizeNodeType constraints violated"); } } @Override public void process(Node externs, Node root) { if (checkOnly) { normalizeNodeTypes(root); } else { // Don't perform "PrepareAnnotations" when doing checks as // they currently aren't valid during sanity checks. In particular, // they DIRECT_EVAL shouldn't be

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> applied after inlining has been // performed. if (externs != null) { NodeTraversal.traverse( compiler, externs, new PrepareAnnotations(compiler)); } if (root != null) { NodeTraversal.traverse( compiler, root, new PrepareAnnotations(compiler)); } } } /** * Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code. */ private void normalizeNodeTypes(Node n) { normalizeBlocks(n); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { // This pass is run during the CompilerTestCase validation, so this // parent pointer check serves as a more general check. Preconditions.checkState(child.getParent() == n); normalizeNodeTypes(child); } } /** * Add blocks to IF, WHILE, DO, etc. */ private void normalizeBlocks(Node n) { if (NodeUtil.isControlStructure(n) && !n.isLabel() && !n.isSwitch()) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (NodeUtil.isControlStructureCodeBlock(n,c) && !c.isBlock()) { Node newBlock = IR.block().srcref(n); n.replaceChild(c, newBlock); if (!c.isEmpty()) { newBlock.addChildrenToFront(c); } else { newBlock.setWasEmptyNode(true); } c = newBlock; reportChange(); } } } } /** * Normalize where annotations appear on the AST. Copies * around existing JSDoc annotations as well as internal annotations. */ static class PrepareAnnotations implements NodeTraversal.Callback { private final CodingConvention convention; PrepareAnnotations(AbstractCompiler compiler) { this.convention = compiler.getCodingConvention(); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isObjectLit()) { normalizeObjectLiteralAnnotations(n); } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>Type.restrictByNotNullOrUndefined(); type = type.restrictByNotNullOrUndefined(); if (!type.canAssignTo(castType) && !castType.canAssignTo(type)) { registerMismatch(type, castType, report(t.makeError(n, INVALID_CAST, castType.toString(), type.toString()))); } } /** * Expect that the given variable has not been declared with a type. * * @param sourceName The name of the source file we're in. * @param n The node where warnings should point to. * @param parent The parent of {@code n}. * @param var The variable that we're checking. * @param variableName The name of the variable. * @param newType The type being applied to the variable. Mostly just here * for the benefit of the warning. * @return The variable we end up with. Most of the time, this will just * be {@code var}, but in some rare cases we will need to declare * a new var with new source info. */ Var expectUndeclaredVariable(String sourceName, CompilerInput input, Node n, Node parent, Var var, String variableName, JSType newType) { Var newVar = var; boolean allowDupe = false; if (n.isGetProp() || NodeUtil.isObjectLitKey(n, parent)) { JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } allowDupe = info != null && info.getSuppressions().contains("duplicate"); } JSType varType = var.getType(); // Only report duplicate declarations that have types. Other duplicates // will be reported by the syntactic scope creator later in the // compilation process. if (varType != null && varType != typeRegistry.getNativeType(UNKNOWN_TYPE) && newType != null && newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) { // If there are two typed declarations of the same variable, that // is an error and the second declaration is ignored, except in the // case of native types. A null input type means that the declaration // was made in Typed

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>ScopeCreator#createInitialScope and is a // native type. We should redeclare it at the new input site. if (var.input == null) { Scope s = var.getScope(); s.undeclare(var); newVar = s.declare(variableName, n, varType, input, false); n.setJSType(varType); if (parent.isVar()) { if (n.getFirstChild() != null) { n.getFirstChild().setJSType(varType); } } else { Preconditions.checkState(parent.isFunction()); parent.setJSType(varType); } } else { // Always warn about duplicates if the overridden type does not // match the original type. // // If the types match, suppress the warning iff there was a @suppress // tag, or if the original declaration was a stub. if (!(allowDupe || var.getParentNode().isExprResult()) || !newType.equals(varType)) { report(JSError.make(sourceName, n, DUP_VAR_DECLARATION, variableName, newType.toString(), var.getInputName(), String.valueOf(var.nameNode.getLineno()), varType.toString())); } } } return newVar; } /** * Expect that all properties on interfaces that this type implements are * implemented and correctly typed. */ void expectAllInterfaceProperties(NodeTraversal t, Node n, FunctionType type) { ObjectType instance = type.getInstanceType(); for (ObjectType implemented : type.getAllImplementedInterfaces()) { if (implemented.getImplicitPrototype() != null) { for (String prop : implemented.getImplicitPrototype().getOwnPropertyNames()) { expectInterfaceProperty(t, n, instance, implemented, prop); } } } } /** * Expect that the peroperty in an interface that this type implements is * implemented and correctly typed. */ private void expectInterfaceProperty(NodeTraversal t, Node n, ObjectType instance, ObjectType implementedInterface, String prop) { if (!instance.hasProperty(prop)) { // Not implemented String sourceName = n.getSourceFileName(); sourceName = sourceName == null ? "" : sourceName; registerMismatch

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>_THIS = DiagnosticType.warning( "JSC_USED_GLOBAL_THIS", "dangerous use of the global 'this' object"); private final AbstractCompiler compiler; /** * If {@code assignLhsChild != null}, then the node being traversed is * a descendant of the first child of an ASSIGN node. assignLhsChild's * parent is this ASSIGN node. */ private Node assignLhsChild = null; CheckGlobalThis(AbstractCompiler compiler) { this.compiler = compiler; } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isFunction()) { // Don't traverse functions that are constructors or have the @this // or @override annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.isInterface() || jsDoc.hasThisType() || jsDoc.isOverride())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; // or // var a = {x: function() {}}; int pType = parent.getType(); if (!(pType == Token.BLOCK || pType == Token.SCRIPT || pType == Token.NAME || pType == Token.ASSIGN || // object literal keys pType == Token.STRING)) { return false; } // Don't traverse functions that are getting lent to a prototype. Node gramps = parent.getParent(); if (NodeUtil.isObjectLitKey(parent, gramps)) { JSDocInfo maybeLends = gramps.getJSDocInfo(); if (maybeLends != null && maybeLends.getLendsName() != null && maybeLends.getLendsName().endsWith(".prototype")) { return false; } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> DiagnosticType BAD_JSDOC_ANNOTATION = DiagnosticType.warning("JSC_BAD_JSDOC_ANNOTATION", "Parse error. {0}"); // A map of Rhino messages to their DiagnosticType. private final Map<Pattern, DiagnosticType> typeMap; private final AbstractCompiler compiler; /** * For each message such as "Not a good use of {0}", replace the place * holder {0} with a wild card that matches all possible strings. * Also put the any non-place-holder in quotes for regex matching later. */ private Pattern replacePlaceHolders(String s) { s = Pattern.quote(s); return Pattern.compile(s.replaceAll("\\{\\d+\\}", "\\\\E.*\\\\Q")); } private RhinoErrorReporter(AbstractCompiler compiler) { this.compiler = compiler; typeMap = ImmutableMap.of( // Extra @fileoverview replacePlaceHolders( ScriptRuntime.getMessage0("msg.jsdoc.fileoverview.extra")), EXTRA_FILEOVERVIEW, // Trailing comma replacePlaceHolders( com.google.javascript.jscomp.mozilla.rhino.ScriptRuntime .getMessage0("msg.extra.trailing.comma")), TRAILING_COMMA, // Duplicate parameter replacePlaceHolders( com.google.javascript.jscomp.mozilla.rhino.ScriptRuntime .getMessage0("msg.dup.parms")), DUPLICATE_PARAM, // Unknown @annotations. replacePlaceHolders(ScriptRuntime.getMessage0("msg.bad.jsdoc.tag")), BAD_JSDOC_ANNOTATION, // Type annotation errors. Pattern.compile("^Bad type annotation.*"), TYPE_PARSE_ERROR); } public static com.google.javascript.jscomp.mozilla.rhino.ErrorReporter forNewRhino(AbstractCompiler compiler) { return new NewRhinoErrorReporter(compiler); } public static ErrorReporter forOldRhino(AbstractCompiler compiler) { return new OldRhinoErrorReporter(compiler); } public void warning(String message, String sourceName, int line, int lineOffset) { compiler.report( makeError(message, sourceName, line, lineOffset, CheckLevel.WARNING

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> public void process(Node externs, Node root) { if (namespace == null) { namespace = new GlobalNamespace(compiler, root); } overrideDefines(collectDefines(root, namespace)); } private void overrideDefines(Map<String, DefineInfo> allDefines) { boolean changed = false; for (Map.Entry<String, DefineInfo> def : allDefines.entrySet()) { String defineName = def.getKey(); DefineInfo info = def.getValue(); Node inputValue = dominantReplacements.get(defineName); Node finalValue = inputValue != null ? inputValue : info.getLastValue(); if (finalValue != info.initialValue) { info.initialValueParent.replaceChild( info.initialValue, finalValue.cloneTree()); compiler.addToDebugLog("Overriding @define variable " + defineName); changed = changed || finalValue.getType() != info.initialValue.getType() || !finalValue.isEquivalentTo(info.initialValue); } } if (changed) { compiler.reportCodeChange(); } Set<String> unusedReplacements = dominantReplacements.keySet(); unusedReplacements.removeAll(allDefines.keySet()); unusedReplacements.removeAll(KNOWN_DEFINES); for (String unknownDefine : unusedReplacements) { compiler.report(JSError.make(UNKNOWN_DEFINE_WARNING, unknownDefine)); } } private static String format(MessageFormat format, Object... params) { return format.format(params); } /** * Only defines of literal number, string, or boolean are supported. */ private boolean isValidDefineType(JSTypeExpression expression) { JSType type = expression.evaluate(null, compiler.getTypeRegistry()); return !type.isUnknownType() && type.isSubtype( compiler.getTypeRegistry().getNativeType( JSTypeNative.NUMBER_STRING_BOOLEAN)); } /** * Finds all defines, and creates a {@link DefineInfo} data structure for * each one. * @return A map of {@link DefineInfo} structures, keyed by name. */ private Map<String, DefineInfo> collectDefines(Node root, GlobalNamespace namespace) { // Find all the global names with a @define annotation

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> List<Name> allDefines = Lists.newArrayList(); for (Name name : namespace.getNameIndex().values()) { Ref decl = name.getDeclaration(); if (name.docInfo != null && name.docInfo.isDefine()) { // Process defines should not depend on check types being enabled, // so we look for the JSDoc instead of the inferred type. if (isValidDefineType(name.docInfo.getType())) { allDefines.add(name); } else { JSError error = JSError.make( decl.getSourceName(), decl.node, INVALID_DEFINE_TYPE_ERROR); compiler.report(error); } } else { for (Ref ref : name.getRefs()) { if (ref == decl) { // Declarations were handled above. continue; } Node n = ref.node; Node parent = ref.node.getParent(); JSDocInfo info = n.getJSDocInfo(); if (info == null && parent.isVar() && parent.hasOneChild()) { info = parent.getJSDocInfo(); } if (info != null && info.isDefine()) { allDefines.add(name); break; } } } } CollectDefines pass = new CollectDefines(compiler, allDefines); NodeTraversal.traverse(compiler, root, pass); return pass.getAllDefines(); } /** * Finds all assignments to @defines, and figures out the last value of * the @define. */ private static final class CollectDefines implements Callback { private final AbstractCompiler compiler; private final Map<String, DefineInfo> assignableDefines; private final Map<String, DefineInfo> allDefines; private final Map<Node, RefInfo> allRefInfo; // A hack that allows us to remove ASSIGN/VAR statements when // we're currently visiting one of the children of the assign. private Node lvalueToRemoveLater = null; // A stack tied to the node traversal, to keep track of whether // we're in a conditional block. If 1 is at the top, assignment to // a define is allowed. Otherwise, it's not allowed. private final Deque<Integer> assignAllowed; CollectDefines(AbstractCompiler compiler, List<

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>Name> listOfDefines) { this.compiler = compiler; this.allDefines = Maps.newHashMap(); assignableDefines = Maps.newHashMap(); assignAllowed = new ArrayDeque<Integer>(); assignAllowed.push(1); // Create a map of references to defines keyed by node for easy lookup allRefInfo = Maps.newHashMap(); for (Name name : listOfDefines) { Ref decl = name.getDeclaration(); if (decl != null) { allRefInfo.put(decl.node, new RefInfo(decl, name)); } for (Ref ref : name.getRefs()) { if (ref == decl) { // Declarations were handled above. continue; } // If there's a TWIN def, only put one of the twins in. if (ref.getTwin() == null || !ref.getTwin().isSet()) { allRefInfo.put(ref.node, new RefInfo(ref, name)); } } } } /** * Get a map of {@link DefineInfo} structures, keyed by the name of * the define. */ Map<String, DefineInfo> getAllDefines() { return allDefines; } /** * Keeps track of whether the traversal is in a conditional branch. * We traverse all nodes of the parse tree. */ @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { updateAssignAllowedStack(n, true); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { RefInfo refInfo = allRefInfo.get(n); if (refInfo != null) { Ref ref = refInfo.ref; Name name = refInfo.name; String fullName = name.getFullName(); switch (ref.type) { case SET_FROM_GLOBAL: case SET_FROM_LOCAL: Node valParent = getValueParent(ref); Node val = valParent.getLastChild(); if (valParent.isAssign() && name.isSimpleName() && name.getDeclaration() == ref) { // For defines, it's an error if a simple name is assigned // before it's declared compiler.report(

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> t.makeError(val, INVALID_DEFINE_INIT_ERROR, fullName)); } else if (processDefineAssignment(t, fullName, val, valParent)) { // remove the assignment so that the variable is still declared, // but no longer assigned to a value, e.g., // DEF_FOO = 5; // becomes "5;" // We can't remove the ASSIGN/VAR when we're still visiting its // children, so we'll have to come back later to remove it. refInfo.name.removeRef(ref); lvalueToRemoveLater = valParent; } break; default: if (t.inGlobalScope()) { // Treat this as a reference to a define in the global scope. // After this point, the define must not be reassigned, // or it's an error. DefineInfo info = assignableDefines.get(fullName); if (info != null) { setDefineInfoNotAssignable(info, t); assignableDefines.remove(fullName); } } break; } } if (!t.inGlobalScope() && n.getJSDocInfo() != null && n.getJSDocInfo().isDefine()) { // warn about @define annotations in local scopes compiler.report( t.makeError(n, NON_GLOBAL_DEFINE_INIT_ERROR, "")); } if (lvalueToRemoveLater == n) { lvalueToRemoveLater = null; if (n.isAssign()) { Node last = n.getLastChild(); n.removeChild(last); parent.replaceChild(n, last); } else { Preconditions.checkState(n.isName()); n.removeChild(n.getFirstChild()); } compiler.reportCodeChange(); } if (n.isCall()) { if (t.inGlobalScope()) { // If there's a function call in the global scope, // we just say it's unsafe and freeze all the defines. // // NOTE(nicksantos): We could be a lot smarter here. For example, // ReplaceOverriddenVars keeps a call graph of all functions and // which functions/variables that they reference, and tries // to statically determine which functions are "safe" and which //

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> { // First declaration of this define. info = new DefineInfo(value, valueParent); allDefines.put(name, info); assignableDefines.put(name, info); } else if (info.recordAssignment(value)) { // The define was already initialized, but this is a safe // re-assignment. return true; } else { // The define was already initialized, and this is an unsafe // re-assignment. compiler.report( t.makeError(valueParent, DEFINE_NOT_ASSIGNABLE_ERROR, name, info.getReasonWhyNotAssignable())); } } return false; } /** * Gets the parent node of the value for any assignment to a Name. * For example, in the assignment * {@code var x = 3;} * the parent would be the NAME node. */ private static Node getValueParent(Ref ref) { // there are two types of declarations: VARs and ASSIGNs return ref.node.getParent() != null && ref.node.getParent().isVar() ? ref.node : ref.node.getParent(); } /** * Records the fact that because of the current node in the node traversal, * the define can't ever be assigned again. * * @param info Represents the define variable. * @param t The current traversal. */ private void setDefineInfoNotAssignable(DefineInfo info, NodeTraversal t) { info.setNotAssignable(format(REASON_DEFINE_NOT_ASSIGNABLE, t.getLineNumber(), t.getSourceName())); } /** * A simple data structure for associating a Ref with the name * that it references. */ private static class RefInfo { final Ref ref; final Name name; RefInfo(Ref ref, Name name) { this.ref = ref; this.name = name; } } } /** * A simple class for storing information about a define. * Gathers the initial value, the last assigned value, and whether * the define can be safely assigned a new value. */ private static final class DefineInfo { public final Node initialValueParent; public final Node initialValue; private Node lastValue; private boolean isAssignable; private String reasonNot

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>, Node n, Node parent) { return include == nodeTypes.contains(n.getType()); } } /** * Creates a node traversal using the specified callback interface. */ public NodeTraversal(AbstractCompiler compiler, Callback cb) { this(compiler, cb, new SyntacticScopeCreator(compiler)); } /** * Creates a node traversal using the specified callback interface * and the scope creator. */ public NodeTraversal(AbstractCompiler compiler, Callback cb, ScopeCreator scopeCreator) { this.callback = cb; if (cb instanceof ScopedCallback) { this.scopeCallback = (ScopedCallback) cb; } this.compiler = compiler; this.inputId = null; this.sourceName = ""; this.scopeCreator = scopeCreator; } private void throwUnexpectedException(Exception unexpectedException) { // If there's an unexpected exception, try to get the // line number of the code that caused it. String message = unexpectedException.getMessage(); // TODO(user): It is possible to get more information if curNode or // its parent is missing. We still have the scope stack in which it is still // very useful to find out at least which function caused the exception. if (inputId != null) { message = unexpectedException.getMessage() + "\n" + formatNodeContext("Node", curNode) + (curNode == null ? "" : formatNodeContext("Parent", curNode.getParent())); } compiler.throwInternalError(message, unexpectedException); } private String formatNodeContext(String label, Node n) { if (n == null) { return " " + label + ": NULL"; } return " " + label + "(" + n.toString(false, false, false) + "): " + formatNodePosition(n); } /** * Traverses a parse tree recursively. */ public void traverse(Node root) { try { inputId = NodeUtil.getInputId(root); sourceName = ""; curNode = root; pushScope(root); traverseBranch(root, null); popScope(); } catch (Exception unexpectedException) { throwUnexpectedException(unexpectedException); } } public void traverseRoots(Node ... roots

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>) { traverseRoots(Lists.newArrayList(roots)); } public void traverseRoots(List<Node> roots) { if (roots.isEmpty()) { return; } try { Node scopeRoot = roots.get(0).getParent(); Preconditions.checkState(scopeRoot != null); inputId = NodeUtil.getInputId(scopeRoot); sourceName = ""; curNode = scopeRoot; pushScope(scopeRoot); for (Node root : roots) { Preconditions.checkState(root.getParent() == scopeRoot); traverseBranch(root, scopeRoot); } popScope(); } catch (Exception unexpectedException) { throwUnexpectedException(unexpectedException); } } private static final String MISSING_SOURCE = "[source unknown]"; private String formatNodePosition(Node n) { if (n == null) { return MISSING_SOURCE + "\n"; } int lineNumber = n.getLineno(); int columnNumber = n.getCharno(); String src = compiler.getSourceLine(sourceName, lineNumber); if (src == null) { src = MISSING_SOURCE; } return sourceName + ":" + lineNumber + ":" + columnNumber + "\n" + src + "\n"; } /** * Traverses a parse tree recursively with a scope, starting with the given * root. This should only be used in the global scope. Otherwise, use * {@link #traverseAtScope}. */ void traverseWithScope(Node root, Scope s) { Preconditions.checkState(s.isGlobal()); inputId = null; sourceName = ""; curNode = root; pushScope(s); traverseBranch(root, null); popScope(); } /** * Traverses a parse tree recursively with a scope, starting at that scope's * root. */ void traverseAtScope(Scope s) { Node n = s.getRootNode(); if (n.isFunction()) { // We need to do some extra magic to make sure that the scope doesn't // get re-created when we dive into the function. if (inputId == null) { inputId = NodeUtil.getInputId(n); } sourceName = getSourceName(n); cur

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>Node = n; pushScope(s); Node args = n.getFirstChild().getNext(); Node body = args.getNext(); traverseBranch(args, n); traverseBranch(body, n); popScope(); } else { traverseWithScope(n, s); } } /** * Traverses an inner node recursively with a refined scope. An inner node may * be any node with a non {@code null} parent (i.e. all nodes except the * root). * * @param node the node to traverse * @param parent the node's parent, it may be not be {@code null} * @param refinedScope the refined scope of the scope currently at the top of * the scope stack or in trivial cases that very scope or {@code null} */ protected void traverseInnerNode(Node node, Node parent, Scope refinedScope) { Preconditions.checkNotNull(parent); if (refinedScope != null && getScope() != refinedScope) { curNode = node; pushScope(refinedScope); traverseBranch(node, parent); popScope(); } else { traverseBranch(node, parent); } } /** * Gets the compiler. */ public Compiler getCompiler() { // TODO(nicksantos): Remove this type cast. This is just temporary // while refactoring. return (Compiler) compiler; } /** * Gets the current line number, or zero if it cannot be determined. The line * number is retrieved lazily as a running time optimization. */ public int getLineNumber() { Node cur = curNode; while (cur != null) { int line = cur.getLineno(); if (line >=0) { return line; } cur = cur.getParent(); } return 0; } /** * Gets the current input source name. * * @return A string that may be empty, but not null */ public String getSourceName() { return sourceName; } /** * Gets the current input source. */ public CompilerInput getInput() { return compiler.getInput(inputId); } /** * Gets the current input module. */ public JSModule getModule()

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> { CompilerInput input = getInput(); return input == null ? null : input.getModule(); } /** Returns the node currently being traversed. */ public Node getCurrentNode() { return curNode; } /** * Traverses a node recursively. */ public static void traverse( AbstractCompiler compiler, Node root, Callback cb) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverse(root); } /** * Traverses a list of node trees. */ public static void traverseRoots( AbstractCompiler compiler, List<Node> roots, Callback cb) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverseRoots(roots); } public static void traverseRoots( AbstractCompiler compiler, Callback cb, Node ... roots) { NodeTraversal t = new NodeTraversal(compiler, cb); t.traverseRoots(roots); } /** * Traverses a branch. */ @SuppressWarnings("fallthrough") private void traverseBranch(Node n, Node parent) { int type = n.getType(); if (type == Token.SCRIPT) { inputId = n.getInputId(); sourceName = getSourceName(n); } curNode = n; if (!callback.shouldTraverse(this, n, parent)) return; switch (type) { case Token.FUNCTION: traverseFunction(n, parent); break; default: for (Node child = n.getFirstChild(); child != null; ) { // child could be replaced, in which case our child node // would no longer point to the true next Node next = child.getNext(); traverseBranch(child, n); child = next; } break; } curNode = n; callback.visit(this, n, parent); } /** * Traverses a function. */ private void traverseFunction(Node n, Node parent) { Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.isFunction()); final Node fnName = n.getFirstChild(); boolean isFunctionExpression = (parent != null) && NodeUtil.isFunctionExpression(n); if (!isFunctionExpression) { // Functions declarations are in the scope containing

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> the declaration. traverseBranch(fnName, n); } curNode = n; pushScope(n); if (isFunctionExpression) { // Function expression names are only accessible within the function // scope. traverseBranch(fnName, n); } final Node args = fnName.getNext(); final Node body = args.getNext(); // Args traverseBranch(args, n); // Body Preconditions.checkState(body.getNext() == null && body.isBlock()); traverseBranch(body, n); popScope(); } /** Examines the functions stack for the last instance of a function node. */ @SuppressWarnings("unchecked") public Node getEnclosingFunction() { if (scopes.size() + scopeRoots.size() < 2) { return null; } else { if (scopeRoots.isEmpty()) { return scopes.peek().getRootNode(); } else { return scopeRoots.peek(); } } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Node node) { Preconditions.checkState(curNode != null); scopeRoots.push(node); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Scope s) { Preconditions.checkState(curNode != null); scopes.push(s); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Pops back to the previous scope (e.g. when leaving a function). */ private void popScope() { if (scopeCallback != null) { scopeCallback.exitScope(this); } if (scopeRoots.isEmpty()) { scopes.pop(); } else { scopeRoots.pop(); } cfgs.pop(); } /** Gets the current scope. */ public Scope getScope() { Scope scope = scopes.isEmpty() ? null : scopes.peek(); if (scopeRoots.isEmpty()) { return scope; } Iterator<Node> it = scopeRoots.descendingIterator();

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> while (it.hasNext()) { scope = scopeCreator.createScope(it.next(), scope); scopes.push(scope); } scopeRoots.clear(); return scope; } /** Gets the control flow graph for the current JS scope. */ public ControlFlowGraph<Node> getControlFlowGraph() { if (cfgs.peek() == null) { ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, true); cfa.process(null, getScopeRoot()); cfgs.pop(); cfgs.push(cfa.getCfg()); } return cfgs.peek(); } /** Returns the current scope's root. */ public Node getScopeRoot() { if (scopeRoots.isEmpty()) { return scopes.peek().getRootNode(); } else { return scopeRoots.peek(); } } /** * Determines whether the traversal is currently in the global scope. */ boolean inGlobalScope() { return getScopeDepth() <= 1; } int getScopeDepth() { return scopes.size() + scopeRoots.size(); } public boolean hasScope() { return !(scopes.isEmpty() && scopeRoots.isEmpty()); } /** Reports a diagnostic (error or warning) */ public void report(Node n, DiagnosticType diagnosticType, String... arguments) { JSError error = JSError.make(getSourceName(), n, diagnosticType, arguments); compiler.report(error); } private static String getSourceName(Node n) { String name = n.getSourceFileName(); return name == null ? "" : name; } InputId getInputId() { return inputId; } /** * Creates a JSError during NodeTraversal. * * @param n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public JSError makeError(Node n, CheckLevel level, DiagnosticType type, String... arguments) { return JSError.make(getSourceName(), n, level, type, arguments); } /** * Creates a JSError during NodeTraversal. * * @param n Determines the line and char position within the source file name * @

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>propdef.hasOneChild()); objectlit.addChildToBack(propdef); } return objectlit; } // TODO(johnlenz): quoted props public static Node propdef(Node string, Node value) { Preconditions.checkState(string.isString()); Preconditions.checkState(!string.hasChildren()); Preconditions.checkState(mayBeExpression(value)); string.addChildToFront(value); return string; } public static Node arraylit(Node ... exprs) { Node arraylit = new Node(Token.ARRAYLIT); for (Node expr : exprs) { Preconditions.checkState(mayBeExpressionOrEmpty(expr)); arraylit.addChildToBack(expr); } return arraylit; } public static Node regexp(Node expr) { Preconditions.checkState(expr.isString()); return new Node(Token.REGEXP, expr); } public static Node regexp(Node expr, Node flags) { Preconditions.checkState(expr.isString()); Preconditions.checkState(flags.isString()); return new Node(Token.REGEXP, expr, flags); } public static Node string(String s) { return Node.newString(s); } public static Node number(double d) { return Node.newNumber(d); } public static Node thisNode() { return new Node(Token.THIS); } public static Node trueNode() { return new Node(Token.TRUE); } public static Node falseNode() { return new Node(Token.FALSE); } public static Node nullNode() { return new Node(Token.NULL); } // helper methods private static Node binaryOp(int token, Node expr1, Node expr2) { Preconditions.checkState(mayBeExpression(expr1)); Preconditions.checkState(mayBeExpression(expr2)); return new Node(token, expr1, expr2); } private static Node unaryOp(int token, Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(token, expr); } private static boolean mayBeExpressionOrEmpty(Node n) { return n.isEmpty() || mayBeExpression(n); } private static boolean isAssignmentTarget(Node n) {

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>.length; i++) { expected[i] = ""; for (CompilerInput input : modules[i].getInputs()) { expected[i] += input.getSourceFile().getCode(); } } test(modules, expected, null, warning); } catch (IOException e) { throw new RuntimeException(e); } } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param compiler A compiler that has been initialized via * {@link Compiler#init} * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected */ protected void test(Compiler compiler, String[] expected, DiagnosticType error, DiagnosticType warning) { test(compiler, expected, error, warning, null); } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param compiler A compiler that has been initialized via * {@link Compiler#init} * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected * @param description The description of the expected warning, * or null if no warning is expected or if the warning's description * should not be examined */ private void test(Compiler compiler, String[] expected, DiagnosticType error, DiagnosticType warning, String description) { RecentChange recentChange = new RecentChange(); compiler.addChangeHandler(recentChange); Node root = compiler.parseInputs(); assertTrue("Unexpected parse error(s): " + Joiner.on("\n").join(compiler.getErrors()), root != null); if (astValidationEnabled) { (new AstValidator()).validateRoot(root); } Node externsRoot = root.getFirstChild(); Node mainRoot

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> = root.getLastChild(); // Save the tree for later comparison. Node rootClone = root.cloneTree(); Node externsRootClone = rootClone.getFirstChild(); Node mainRootClone = rootClone.getLastChild(); int numRepetitions = getNumRepetitions(); ErrorManager[] errorManagers = new ErrorManager[numRepetitions]; int aggregateWarningCount = 0; List<JSError> aggregateWarnings = Lists.newArrayList(); boolean hasCodeChanged = false; assertFalse("Code should not change before processing", recentChange.hasCodeChanged()); for (int i = 0; i < numRepetitions; ++i) { if (compiler.getErrorCount() == 0) { errorManagers[i] = new BlackHoleErrorManager(compiler); // Only run the type checking pass once, if asked. // Running it twice can cause unpredictable behavior because duplicate // objects for the same type are created, and the type system // uses reference equality to compare many types. if (typeCheckEnabled && i == 0) { TypeCheck check = createTypeCheck(compiler, typeCheckLevel); check.processForTesting(externsRoot, mainRoot); } // Only run the normalize pass once, if asked. if (normalizeEnabled && i == 0) { normalizeActualCode(compiler, externsRoot, mainRoot); } if (markNoSideEffects && i == 0) { MarkNoSideEffectCalls mark = new MarkNoSideEffectCalls(compiler); mark.process(externsRoot, mainRoot); } recentChange.reset(); getProcessor(compiler).process(externsRoot, mainRoot); if (astValidationEnabled) { (new AstValidator()).validateRoot(root); } if (checkLineNumbers) { (new LineNumberCheck(compiler)).process(externsRoot, mainRoot); } hasCodeChanged = hasCodeChanged || recentChange.hasCodeChanged(); aggregateWarningCount += errorManagers[i].getWarningCount(); aggregateWarnings.addAll(Lists.newArrayList(compiler.getWarnings())); if (normalizeEnabled) { boolean verifyDeclaredConstants = true; new Normalize.VerifyConstants(compiler, verifyDeclaredConstants) .process(externsRoot, mainRoot); }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>ternsChanges) { String explanation = externsRootClone.checkTreeEquals(externsRoot); fail("Unexpected changes to externs" + "\nExpected: " + compiler.toSource(externsRootClone) + "\nResult: " + compiler.toSource(externsRoot) + "\n" + explanation); } if (!codeChange && !externsChange) { assertFalse( "compiler.reportCodeChange() was called " + "even though nothing changed", hasCodeChanged); } else { assertTrue("compiler.reportCodeChange() should have been called", hasCodeChanged); } if (compareAsTree) { String explanation = expectedRoot.checkTreeEquals(mainRoot); assertNull("\nExpected: " + compiler.toSource(expectedRoot) + "\nResult: " + compiler.toSource(mainRoot) + "\n" + explanation, explanation); } else if (expected != null) { assertEquals( Joiner.on("").join(expected), compiler.toSource(mainRoot)); } // Verify normalization is not invalidated. Node normalizeCheckRootClone = root.cloneTree(); Node normalizeCheckExternsRootClone = root.getFirstChild(); Node normalizeCheckMainRootClone = root.getLastChild(); new PrepareAst(compiler).process( normalizeCheckExternsRootClone, normalizeCheckMainRootClone); String explanation = normalizeCheckMainRootClone.checkTreeEquals(mainRoot); assertNull("Node structure normalization invalidated.\nExpected: " + compiler.toSource(normalizeCheckMainRootClone) + "\nResult: " + compiler.toSource(mainRoot) + "\n" + explanation, explanation); // TODO(johnlenz): enable this for most test cases. // Currently, this invalidates test for while-loops, for-loop // initializers, and other naming. However, a set of code // (FoldConstants, etc) runs before the Normalize pass, so this can't be // force on everywhere. if (normalizeEnabled) { new Normalize(compiler, true).process( normalizeCheckExternsRootClone, normalizeCheckMainRootClone); explanation = normalizeCheckMainRootClone.checkTreeEquals(mainRoot); assertNull("Normalization invalidated.\

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>nExpected: " + compiler.toSource(normalizeCheckMainRootClone) + "\nResult: " + compiler.toSource(mainRoot) + "\n" + explanation, explanation); } } else { String errors = ""; for (JSError actualError : compiler.getErrors()) { errors += actualError.description + "\n"; } assertEquals("There should be one error. " + errors, 1, compiler.getErrorCount()); assertEquals(errors, error, compiler.getErrors()[0].getType()); if (warning != null) { String warnings = ""; for (JSError actualError : compiler.getWarnings()) { warnings += actualError.description + "\n"; } assertEquals("There should be one warning. " + warnings, 1, compiler.getWarningCount()); assertEquals(warnings, warning, compiler.getWarnings()[0].getType()); } } } private void normalizeActualCode( Compiler compiler, Node externsRoot, Node mainRoot) { Normalize normalize = new Normalize(compiler, false); normalize.process(externsRoot, mainRoot); } /** * Parses expected js inputs and returns the root of the parse tree. */ protected Node parseExpectedJs(String[] expected) { Compiler compiler = createCompiler(); JSSourceFile[] inputs = new JSSourceFile[expected.length]; for (int i = 0; i < expected.length; i++) { inputs[i] = JSSourceFile.fromCode("expected" + i, expected[i]); } compiler.init(externsInputs, inputs, getOptions()); Node root = compiler.parseInputs(); assertTrue("Unexpected parse error(s): " + Joiner.on("\n").join(compiler.getErrors()), root != null); Node externsRoot = root.getFirstChild(); Node mainRoot = externsRoot.getNext(); // Only run the normalize pass, if asked. if (normalizeEnabled && normalizeExpected && !compiler.hasErrors()) { Normalize normalize = new Normalize(compiler, false); normalize.process(externsRoot, mainRoot); } return mainRoot; } protected Node parseExpectedJs(String expected) { return parseExpectedJs(new String[] {expected}); } /**

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> (int i = 0; i < inputs.length; i++) { JSModule module = modules[i] = new JSModule("m" + i); module.add(JSSourceFile.fromCode("i" + i, inputs[i])); } return modules; } private static class BlackHoleErrorManager extends BasicErrorManager { private BlackHoleErrorManager(Compiler compiler) { compiler.setErrorManager(this); } @Override public void println(CheckLevel level, JSError error) {} @Override public void printSummary() {} } Compiler createCompiler() { Compiler compiler = new Compiler(); return compiler; } protected void setExpectedSymbolTableError(DiagnosticType type) { this.expectedSymbolTableError = type; } /** Finds the first matching qualified name node in post-traversal order. */ protected final Node findQualifiedNameNode(final String name, Node root) { final List<Node> matches = Lists.newArrayList(); NodeUtil.visitPostOrder(root, new NodeUtil.Visitor() { @Override public void visit(Node n) { if (name.equals(n.getQualifiedName())) { matches.add(n); } } }, Predicates.<Node>alwaysTrue()); return matches.get(0); } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>public interface StaticScope<T> { /** * Returns the root node associated with this scope. May be null. */ Node getRootNode(); /** Returns the scope enclosing this one or null if none. */ StaticScope<T> getParentScope(); /** * Returns any defined slot within this scope for this name. This call * continues searching through parent scopes if a slot with this name is not * found in the current scope. * @param name The name of the variable slot to look up. * @return The defined slot for the variable, or {@code null} if no * definition exists. */ StaticSlot<T> getSlot(String name); /** Like {@code getSlot} but does not recurse into parent scopes. */ StaticSlot<T> getOwnSlot(String name); /** Returns the expected type of {@code this} in the current scope. */ T getTypeOfThis(); }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>); /** * Gets the central registry of type violations. */ abstract TypeValidator getTypeValidator(); /** * Parses code for injecting. */ abstract Node parseSyntheticCode(String code); /** * Parses code for injecting, and associate it with a given source file. */ abstract Node parseSyntheticCode(String filename, String code); /** * Parses code for testing. */ abstract Node parseTestCode(String code); /** * Prints a node to source code. */ abstract String toSource(Node root); /** * Gets a default error reporter for injecting into Rhino. */ abstract ErrorReporter getDefaultErrorReporter(); /** * Get an interpreter for type analysis. */ public abstract ReverseAbstractInterpreter getReverseAbstractInterpreter(); /** * @return The current life-cycle stage of the AST we're working on. */ LifeCycleStage getLifeCycleStage() { return stage; } /** * Generates unique ids. */ abstract Supplier<String> getUniqueNameIdSupplier(); /** * @return Whether any errors have been encountered that * should stop the compilation process. */ abstract boolean hasHaltingErrors(); /** * Register a listener for code change events. */ abstract void addChangeHandler(CodeChangeHandler handler); /** * Remove a listener for code change events. */ abstract void removeChangeHandler(CodeChangeHandler handler); /** * Returns true if compiling in IDE mode. */ abstract boolean isIdeMode(); /** * @return Whether the compiler is in ES5Mode. */ abstract boolean acceptEcmaScript5(); /** * @return Whether the compiler accepts `const' keyword. */ abstract boolean acceptConstKeyword(); /** * Returns the parser configuration. */ abstract Config getParserConfig(); /** * Returns true if type checking is enabled. */ abstract boolean isTypeCheckingEnabled(); /** * Normalizes the types of AST nodes in the given tree, and * annotates any nodes to which the coding convention applies so that passes * can read the annotations instead of using the coding convention. */ abstract void prepareAst(Node root); /** * Gets the error manager. */ abstract public ErrorManager getErrorManager(); /** *

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>( SourceMapGeneratorFactory.getInstance(SourceMapFormat.V3)); } }; abstract SourceMap getInstance(); } /** * Source maps can be very large different levels of detail can be specified. */ public static enum DetailLevel implements Predicate<Node> { // ALL is best when the fullest details are needed for debugging or for // code-origin analysis. ALL { @Override public boolean apply(Node node) { return true; } }, // SYMBOLS is intended to be used for stack trace deobfuscation when full // detail is not needed. SYMBOLS { @Override public boolean apply(Node node) { return node.isCall() || node.isNew() || node.isFunction() || node.isName() || NodeUtil.isGet(node) || NodeUtil.isObjectLitKey(node, node.getParent()) || (node.isString() && NodeUtil.isGet(node.getParent())); } }; } public static class LocationMapping { final String prefix; final String replacement; public LocationMapping(String prefix, String replacement) { this.prefix = prefix; this.replacement = replacement; } } private final SourceMapGenerator generator; private List<LocationMapping> prefixMappings = Collections.emptyList(); private final Map<String, String> sourceLocationFixupCache = Maps.newHashMap(); private SourceMap(SourceMapGenerator generator) { this.generator = generator; } public void addMapping( Node node, FilePosition outputStartPosition, FilePosition outputEndPosition) { String sourceFile = node.getSourceFileName(); // If the node does not have an associated source file or // its line number is -1, then the node does not have sufficient // information for a mapping to be useful. if (sourceFile == null || node.getLineno() < 0) { return; } sourceFile = fixupSourceLocation(sourceFile); String originalName = (String) node.getProp(Node.ORIGINALNAME_PROP); // Strangely, Rhino source lines are one based but columns are // zero based. // We don't change this for the v1 or v2 source maps but for // v3 we make them

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> both 0 based. int lineBaseOffset = 1; if (generator instanceof SourceMapGeneratorV1 || generator instanceof SourceMapGeneratorV2) { lineBaseOffset = 0; } generator.addMapping( sourceFile, originalName, new FilePosition(node.getLineno() - lineBaseOffset, node.getCharno()), outputStartPosition, outputEndPosition); } /** * @param sourceFile The source file location to fixup. * @return a remapped source file. */ private String fixupSourceLocation(String sourceFile) { if (prefixMappings.isEmpty()) { return sourceFile; } String fixed = sourceLocationFixupCache.get(sourceFile); if (fixed != null) { return fixed; } // Replace the first prefix found with its replacement for (LocationMapping mapping : prefixMappings) { if (sourceFile.startsWith(mapping.prefix)) { fixed = mapping.replacement + sourceFile.substring( mapping.prefix.length()); break; } } // If none of the mappings match then use the original file path. if (fixed == null) { fixed = sourceFile; } sourceLocationFixupCache.put(sourceFile, fixed); return fixed; } public void appendTo(Appendable out, String name) throws IOException { generator.appendTo(out, name); } public void reset() { generator.reset(); sourceLocationFixupCache.clear(); } public void setStartingPosition(int offsetLine, int offsetIndex) { generator.setStartingPosition(offsetLine, offsetIndex); } public void setWrapperPrefix(String prefix) { generator.setWrapperPrefix(prefix); } public void validate(boolean validate) { generator.validate(validate); } /** * @param sourceMapLocationMappings */ public void setPrefixMappings(List<LocationMapping> sourceMapLocationMappings) { this.prefixMappings = sourceMapLocationMappings; } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> * Lists of modules at each depth. <code>modulesByDepth.get(3)</code> is a * list of the modules at depth 3, for example. */ private List<List<JSModule>> modulesByDepth; /** * dependencyMap is a cache of dependencies that makes the dependsOn * function faster. Each map entry associates a starting * JSModule with the set of JSModules that are transitively dependent on the * starting module. * * If the cache returns null, then the entry hasn't been filled in for that * module. * * dependencyMap should be filled from leaf to root so that * getTransitiveDepsDeepestFirst can use its results directly. */ private Map<JSModule, Set<JSModule>> dependencyMap = Maps.newHashMap(); /** * Creates a module graph from a list of modules in dependency order. */ public JSModuleGraph(JSModule[] modulesInDepOrder) { this(Lists.<JSModule>newArrayList(modulesInDepOrder)); } /** * Creates a module graph from a list of modules in dependency order. */ public JSModuleGraph(List<JSModule> modulesInDepOrder) { modules = Sets.newHashSetWithExpectedSize(modulesInDepOrder.size()); modulesByDepth = Lists.newArrayList(); for (JSModule module : modulesInDepOrder) { int depth = 0; for (JSModule dep : module.getDependencies()) { int depDepth = dep.getDepth(); if (depDepth < 0) { throw new ModuleDependenceException(String.format( "Modules not in dependency order: %s preceded %s", module.getName(), dep.getName()), module, dep); } depth = Math.max(depth, depDepth + 1); } module.setDepth(depth); modules.add(module); if (depth == modulesByDepth.size()) { modulesByDepth.add(new ArrayList<JSModule>()); } modulesByDepth.get(depth).add(module); } } /** * Gets an iterable over all modules. */ Iterable<JSModule> getAllModules() { return modules; } /** * Gets all the modules in dependency order. Modules

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> with the same depth * will be ordered deterministically. */ Iterable<JSModule> getAllModulesInDependencyOrder() { List<JSModule> modules = Lists.newArrayList(getAllModules()); Collections.sort(modules, new DepthComparator()); return modules; } /** * Gets the total number of modules. */ int getModuleCount() { return modules.size(); } /** * Gets the root module. */ JSModule getRootModule() { return Iterables.getOnlyElement(modulesByDepth.get(0)); } /** * Determines whether this module depends on a given module. Note that a * module never depends on itself, as that dependency would be cyclic. */ public boolean dependsOn(JSModule src, JSModule m) { Set<JSModule> deps = dependencyMap.get(src); if (deps == null) { deps = getTransitiveDepsDeepestFirst(src); dependencyMap.put(src, deps); } return deps.contains(m); } /** * Finds the deepest common dependency of two modules, not including the two * modules themselves. * * @param m1 A module in this graph * @param m2 A module in this graph * @return The deepest common dep of {@code m1} and {@code m2}, or null if * they have no common dependencies */ JSModule getDeepestCommonDependency(JSModule m1, JSModule m2) { int m1Depth = m1.getDepth(); int m2Depth = m2.getDepth(); // According our definition of depth, the result must have a strictly // smaller depth than either m1 or m2. for (int depth = Math.min(m1Depth, m2Depth) - 1; depth >= 0; depth--) { List<JSModule> modulesAtDepth = modulesByDepth.get(depth); // Look at the modules at this depth in reverse order, so that we use the // original ordering of the modules to break ties (later meaning deeper). for (int i = modulesAtDepth.size() - 1; i >= 0; i--) { JSModule m = modulesAtDepth.get(i); if

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> String> graphViz = LinkedDirectedGraph.create(); for (JSModule module : getAllModulesInDependencyOrder()) { graphViz.createNode(module); for (JSModule dep : module.getDependencies()) { graphViz.createNode(dep); graphViz.connect(module, "->", dep); } } return graphViz; } /** * A module depth comparator that considers a deeper module to be * "greater than" a shallower module. Uses module names to * consistently break ties. */ private class DepthComparator implements Comparator<JSModule> { @Override public int compare(JSModule m1, JSModule m2) { return depthCompare(m1, m2); } } /** * A module depth comparator that considers a deeper module to be "less than" * a shallower module. Uses module names to consistently break ties. */ private class InverseDepthComparator implements Comparator<JSModule> { @Override public int compare(JSModule m1, JSModule m2) { return depthCompare(m2, m1); } } private int depthCompare(JSModule m1, JSModule m2) { if (m1 == m2) { return 0; } int d1 = m1.getDepth(); int d2 = m2.getDepth(); return d1 < d2 ? -1 : d2 == d1 ? m1.getName().compareTo(m2.getName()) : 1; } /* * Exception class for declaring when the modules being fed into a * JSModuleGraph as input aren't in dependence order, and so can't be * processed for caching of various dependency-related queries. */ protected static class ModuleDependenceException extends IllegalArgumentException { private static final long serialVersionUID = 1; private final JSModule module; private final JSModule dependentModule; protected ModuleDependenceException(String message, JSModule module, JSModule dependentModule) { super(message); this.module = module; this.dependentModule = dependentModule; } public JSModule getModule() { return module; } public JSModule getDependentModule() { return dependentModule; } } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> {1} because it references {2}"); static final DiagnosticType STRICT_MODULE_DEP_ERROR = DiagnosticType.disabled( "JSC_STRICT_MODULE_DEPENDENCY", "module {0} cannot reference {2}, defined in " + "module {1}"); static final DiagnosticType NAME_REFERENCE_IN_EXTERNS_ERROR = DiagnosticType.warning( "JSC_NAME_REFERENCE_IN_EXTERNS", "accessing name {0} in externs has no effect"); static final DiagnosticType UNDEFINED_EXTERN_VAR_ERROR = DiagnosticType.warning( "JSC_UNDEFINED_EXTERN_VAR_ERROR", "name {0} is not undefined in the externs."); static final DiagnosticType INVALID_FUNCTION_DECL = DiagnosticType.error("JSC_INVALID_FUNCTION_DECL", "Syntax error: function declaration must have a name"); private CompilerInput synthesizedExternsInput = null; private Node synthesizedExternsRoot = null; // Vars that still need to be declared in externs. These will be declared // at the end of the pass, or when we see the equivalent var declared // in the normal code. private final Set<String> varsToDeclareInExterns = Sets.newHashSet(); private final AbstractCompiler compiler; // Whether this is the post-processing sanity check. private final boolean sanityCheck; // Whether extern checks emit error. private final boolean strictExternCheck; VarCheck(AbstractCompiler compiler) { this(compiler, false); } VarCheck(AbstractCompiler compiler, boolean sanityCheck) { this.compiler = compiler; this.strictExternCheck = compiler.getErrorLevel( JSError.make("", 0, 0, UNDEFINED_EXTERN_VAR_ERROR)) == CheckLevel.ERROR; this.sanityCheck = sanityCheck; } @Override public void process(Node externs, Node root) { // Don't run externs-checking in sanity check mode. Normalization will // remove duplicate VAR declarations, which will make // externs look like they have assigns. if (!sanityCheck) { NodeTraversal.traverse(compiler, externs, new NameRefInExternsCheck()); } NodeTraversal.traverseRoots( compiler

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>, Lists.newArrayList(externs, root), this); for (String varName : varsToDeclareInExterns) { createSynthesizedExternVar(varName); } } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { Preconditions.checkState(scriptRoot.isScript()); NodeTraversal t = new NodeTraversal(compiler, this); // Note we use the global scope to prevent wrong "undefined-var errors" on // variables that are defined in other js files. t.traverseWithScope(scriptRoot, SyntacticScopeCreator.generateUntypedTopScope(compiler)); // TODO(bashir) Check if we need to createSynthesizedExternVar like process. } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!n.isName()) { return; } String varName = n.getString(); // Only a function can have an empty name. if (varName.isEmpty()) { Preconditions.checkState(parent.isFunction()); // A function declaration with an empty name passes Rhino, // but is supposed to be a syntax error according to the spec. if (!NodeUtil.isFunctionExpression(parent)) { t.report(n, INVALID_FUNCTION_DECL); } return; } // Check if this is a declaration for a var that has been declared // elsewhere. If so, mark it as a duplicate. if ((parent.isVar() || NodeUtil.isFunctionDeclaration(parent)) && varsToDeclareInExterns.contains(varName)) { createSynthesizedExternVar(varName); n.addSuppression("duplicate"); } // Check that the var has been declared. Scope scope = t.getScope(); Scope.Var var = scope.getVar(varName); if (var == null) { if (NodeUtil.isFunctionExpression(parent)) { // e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the // current scope. } else { // The extern checks are stricter, don't report a second error. if (!strictExternCheck || !t.getInput().isExtern()) { t.report(n, UNDEFINED_VAR_ERROR, var

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> module name (primarily for debugging). */ @Override public String toString() { return name; } /** * Removes any references to nodes of the AST. This method is needed to * allow the ASTs to be garbage collected if the modules are kept around. */ public void clearAsts() { for (CompilerInput input : inputs) { input.clearAst(); } } /** * Puts the JS files into a topologically sorted order by their dependencies. */ public void sortInputsByDeps(Compiler compiler) { // Set the compiler, so that we can parse requires/provides and report // errors properly. for (CompilerInput input : inputs) { input.setCompiler(compiler); } // Sort the JSModule in this order. try { List<CompilerInput> sortedList = (new SortedDependencies<CompilerInput>( Collections.<CompilerInput>unmodifiableList(inputs))) .getSortedList(); inputs.clear(); inputs.addAll(sortedList); } catch (CircularDependencyException e) { compiler.report( JSError.make(CIRCULAR_DEPENDENCY_ERROR, e.getMessage())); } } /** * Returns the given collection of modules in topological order. * * Note that this will return the modules in the same order if they are * already sorted, and in general, will only change the order as necessary to * satisfy the ordering dependencies. This can be important for cases where * the modules do not properly specify all dependencies. */ public static JSModule[] sortJsModules(Collection<JSModule> modules) throws CircularDependencyException { // Sort the JSModule in this order. List<JSModule> sortedList = (new SortedDependencies<JSModule>( Lists.newArrayList(modules))).getSortedList(); return sortedList.toArray(new JSModule[sortedList.size()]); } /** * @param dep the depth to set */ public void setDepth(int dep) { this.depth = dep; } /** * @return the depth */ public int getDepth() { return depth; } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>process(Node, Node)}. * * @author kushal@google.com (Kushal Dave) */ class ReferenceCollectingCallback implements ScopedCallback, HotSwapCompilerPass, StaticSymbolTable<Var, ReferenceCollectingCallback.Reference> { /** * Maps a given variable to a collection of references to that name. Note that * Var objects are not stable across multiple traversals (unlike scope root or * name). */ private final Map<Var, ReferenceCollection> referenceMap = Maps.newHashMap(); /** * The stack of basic blocks and scopes the current traversal is in. */ private final Deque<BasicBlock> blockStack = new ArrayDeque<BasicBlock>(); /** * Source of behavior at various points in the traversal. */ private final Behavior behavior; /** * Javascript compiler to use in traversing. */ private final AbstractCompiler compiler; /** * Only collect references for filtered variables. */ private final Predicate<Var> varFilter; /** * Constructor initializes block stack. */ ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior) { this(compiler, behavior, Predicates.<Var>alwaysTrue()); } /** * Constructor only collects references that match the given variable. * * The test for Var equality uses reference equality, so it's necessary to * inject a scope when you traverse. */ ReferenceCollectingCallback(AbstractCompiler compiler, Behavior behavior, Predicate<Var> varFilter) { this.compiler = compiler; this.behavior = behavior; this.varFilter = varFilter; } /** * Convenience method for running this pass over a tree with this * class as a callback. */ @Override public void process(Node externs, Node root) { NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } /** * Same as process but only runs on a part of AST associated to one script. */ @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { NodeTraversal.traverse(compiler, scriptRoot, this); } /** * Gets the variables that were referenced in this callback. */ @Override public Iterable<Var> getAllSymbols() { return referenceMap

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>.keySet(); } @Override public Scope getScope(Var var) { return var.scope; } /** * Gets the reference collection for the given variable. */ @Override public ReferenceCollection getReferences(Var v) { return referenceMap.get(v); } /** * For each node, update the block stack and reference collection * as appropriate. */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { Var v; if (n.getString().equals("arguments")) { v = t.getScope().getArgumentsVar(); } else { v = t.getScope().getVar(n.getString()); } if (v != null && varFilter.apply(v)) { addReference(t, v, new Reference(n, t, blockStack.peek())); } } if (isBlockBoundary(n, parent)) { blockStack.pop(); } } /** * Updates block stack and invokes any additional behavior. */ @Override public void enterScope(NodeTraversal t) { Node n = t.getScope().getRootNode(); BasicBlock parent = blockStack.isEmpty() ? null : blockStack.peek(); blockStack.push(new BasicBlock(parent, n)); } /** * Updates block statck and invokes any additional behavior. */ @Override public void exitScope(NodeTraversal t) { blockStack.pop(); if (t.getScope().isGlobal()) { // Update global scope reference lists when we are done with it. compiler.updateGlobalVarReferences(referenceMap, t.getScopeRoot()); behavior.afterExitScope(t, compiler.getGlobalVarReferences()); } else { behavior.afterExitScope(t, new ReferenceMapWrapper(referenceMap)); } } /** * Updates block stack. */ @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { // If node is a new basic block, put on basic block stack if (isBlockBoundary(n, parent)) { blockStack.push(new BasicBlock(blockStack.peek(), n)); } return true; } /** * @

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>return true if this node marks the start of a new basic block */ private static boolean isBlockBoundary(Node n, Node parent) { if (parent != null) { switch (parent.getType()) { case Token.DO: case Token.FOR: case Token.TRY: case Token.WHILE: case Token.WITH: // NOTE: TRY has up to 3 child blocks: // TRY // BLOCK // BLOCK // CATCH // BLOCK // Note that there is an explcit CATCH token but no explicit // FINALLY token. For simplicity, we consider each BLOCK // a separate basic BLOCK. return true; case Token.AND: case Token.HOOK: case Token.IF: case Token.OR: // The first child of a conditional is not a boundary, // but all the rest of the children are. return n != parent.getFirstChild(); } } return n.isCase(); } private void addReference(NodeTraversal t, Var v, Reference reference) { // Create collection if none already ReferenceCollection referenceInfo = referenceMap.get(v); if (referenceInfo == null) { referenceInfo = new ReferenceCollection(); referenceMap.put(v, referenceInfo); } // Add this particular reference referenceInfo.add(reference, t, v); } interface ReferenceMap { ReferenceCollection getReferences(Var var); } private static class ReferenceMapWrapper implements ReferenceMap { private final Map<Var, ReferenceCollection> referenceMap; public ReferenceMapWrapper(Map<Var, ReferenceCollection> referenceMap) { this.referenceMap = referenceMap; } @Override public ReferenceCollection getReferences(Var var) { return referenceMap.get(var); } } /** * Way for callers to add specific behavior during traversal that * utilizes the built-up reference information. */ interface Behavior { /** * Called after we finish with a scope. */ void afterExitScope(NodeTraversal t, ReferenceMap referenceMap); } static Behavior DO_NOTHING_BEHAVIOR = new Behavior() { @Override public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {} }; /** * A collection of

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> references. Can be subclassed to apply checks or * store additional state when adding. */ static class ReferenceCollection implements Iterable<Reference> { List<Reference> references = Lists.newArrayList(); @Override public Iterator<Reference> iterator() { return references.iterator(); } void add(Reference reference, NodeTraversal t, Var v) { references.add(reference); } /** * Determines if the variable for this reference collection is * "well-defined." A variable is well-defined if we can prove at * compile-time that it's assigned a value before it's used. * * Notice that if this function returns false, this doesn't imply that the * variable is used before it's assigned. It just means that we don't * have enough information to make a definitive judgement. */ protected boolean isWellDefined() { int size = references.size(); if (size == 0) { return false; } // If this is a declaration that does not instantiate the variable, // it's not well-defined. Reference init = getInitializingReference(); if (init == null) { return false; } Preconditions.checkState(references.get(0).isDeclaration()); BasicBlock initBlock = init.getBasicBlock(); for (int i = 1; i < size; i++) { if (!initBlock.provablyExecutesBefore( references.get(i).getBasicBlock())) { return false; } } return true; } /** * Whether the variable is escaped into an inner scope. */ boolean isEscaped() { Scope scope = null; for (Reference ref : references) { if (scope == null) { scope = ref.scope; } else if (scope != ref.scope) { return true; } } return false; } /** * @param index The index into the references array to look for an * assigning declaration. * * This is either the declaration if a value is assigned (such as * "var a = 2", "function a()...", "... catch (a)..."). */ private boolean isInitializingDeclarationAt(int index) { Reference maybeInit = references.get(index); if (maybeInit

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>.isInitializingDeclaration()) { // This is a declaration that represents the initial value. // Specifically, var declarations without assignments such as "var a;" // are not. return true; } return false; } /** * @param index The index into the references array to look for an * initialized assignment reference. That is, an assignment immediately * follow a variable declaration that itself does not initialize the * variable. */ private boolean isInitializingAssignmentAt(int index) { if (index < references.size() && index > 0) { Reference maybeDecl = references.get(index-1); if (maybeDecl.isVarDeclaration()) { Preconditions.checkState(!maybeDecl.isInitializingDeclaration()); Reference maybeInit = references.get(index); if (maybeInit.isSimpleAssignmentToName()) { return true; } } } return false; } /** * @return The reference that provides the value for the variable at the * time of the first read, if known, otherwise null. * * This is either the variable declaration ("var a = ...") or first * reference following the declaration if it is an assignment. */ Reference getInitializingReference() { if (isInitializingDeclarationAt(0)) { return references.get(0); } else if (isInitializingAssignmentAt(1)) { return references.get(1); } return null; } /** * Constants are allowed to be defined after their first use. */ Reference getInitializingReferenceForConstants() { int size = references.size(); for (int i = 0; i < size; i++) { if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) { return references.get(i); } } return null; } /** * @return Whether the variable is only assigned a value once for its * lifetime. */ boolean isAssignedOnceInLifetime() { Reference ref = getOneAndOnlyAssignment(); if (ref == null) { return false; } // Make sure this assignment is not in a loop. for (BasicBlock block = ref.getBasicBlock(); block != null; block = block.getParent()) { if (block.isFunction) { break

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>; } else if (block.isLoop) { return false; } } return true; } /** * @return The one and only assignment. Returns if there are 0 or 2+ * assignments. */ private Reference getOneAndOnlyAssignment() { Reference assignment = null; int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { if (assignment == null) { assignment = ref; } else { return null; } } } return assignment; } /** * @return Whether the variable is never assigned a value. */ boolean isNeverAssigned() { int size = references.size(); for (int i = 0; i < size; i++) { Reference ref = references.get(i); if (ref.isLvalue() || ref.isInitializingDeclaration()) { return false; } } return true; } boolean firstReferenceIsAssigningDeclaration() { int size = references.size(); if (size > 0 && references.get(0).isInitializingDeclaration()) { return true; } return false; } } /** * Represents a single declaration or reference to a variable. */ static final class Reference implements StaticReference<JSType> { private static final Set<Integer> DECLARATION_PARENTS = ImmutableSet.of(Token.VAR, Token.FUNCTION, Token.CATCH); private final Node nameNode; private final BasicBlock basicBlock; private final Scope scope; private final InputId inputId; private final StaticSourceFile sourceFile; Reference(Node nameNode, NodeTraversal t, BasicBlock basicBlock) { this(nameNode, basicBlock, t.getScope(), t.getInput().getInputId()); } // Bleeding functions are weird, because the declaration does // not appear inside their scope. So they need their own constructor. static Reference newBleedingFunction(NodeTraversal t, BasicBlock basicBlock, Node func) { return new Reference(func.getFirstChild(), basicBlock, t.getScope(), t.

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> BasicBlock getBasicBlock() { return basicBlock; } Node getParent() { return getNode().getParent(); } Node getGrandparent() { Node parent = getParent(); return parent == null ? null : parent.getParent(); } private static boolean isLhsOfForInExpression(Node n) { Node parent = n.getParent(); if (parent.isVar()) { return isLhsOfForInExpression(parent); } return NodeUtil.isForIn(parent) && parent.getFirstChild() == n; } boolean isSimpleAssignmentToName() { Node parent = getParent(); return parent.isAssign() && parent.getFirstChild() == nameNode; } boolean isLvalue() { Node parent = getParent(); int parentType = parent.getType(); return (parentType == Token.VAR && nameNode.getFirstChild() != null) || parentType == Token.INC || parentType == Token.DEC || (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == nameNode) || isLhsOfForInExpression(nameNode); } Scope getScope() { return scope; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ private final boolean isFunction; /** * Whether this block denotes a loop. */ private final boolean isLoop; /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); this.isFunction = root.isFunction(); if (root.getParent() != null) { int pType = root.getParent().getType(); this.isLoop

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> the node is not typeable. boolean typeable = true; switch (n.getType()) { case Token.NAME: typeable = visitName(t, n, parent); break; case Token.PARAM_LIST: // If this is under a FUNCTION node, it is a parameter list and can be // ignored here. if (!parent.isFunction()) { ensureTyped(t, n, getJSType(n.getFirstChild())); } else { typeable = false; } break; case Token.COMMA: ensureTyped(t, n, getJSType(n.getLastChild())); break; case Token.TRUE: case Token.FALSE: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.THIS: ensureTyped(t, n, t.getScope().getTypeOfThis()); break; case Token.NULL: ensureTyped(t, n, NULL_TYPE); break; case Token.NUMBER: ensureTyped(t, n, NUMBER_TYPE); break; case Token.STRING: // Object literal keys are handled with OBJECTLIT if (!NodeUtil.isObjectLitKey(n, n.getParent())) { ensureTyped(t, n, STRING_TYPE); } else { // Object literal keys are not typeable typeable = false; } break; case Token.GETTER_DEF: case Token.SETTER_DEF: // Object literal keys are handled with OBJECTLIT break; case Token.ARRAYLIT: ensureTyped(t, n, ARRAY_TYPE); break; case Token.REGEXP: ensureTyped(t, n, REGEXP_TYPE); break; case Token.GETPROP: visitGetProp(t, n, parent); typeable = !(parent.isAssign() && parent.getFirstChild() == n); break; case Token.GETELEM: visitGetElem(t, n); // The type of GETELEM is always unknown, so no point counting that. // If that unknown leaks elsewhere (say by an assignment to another // variable), then it will be counted. typeable = false; break; case Token.VAR: visitVar(t, n); typeable = false;

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>(FunctionType ctor) { Preconditions.checkArgument(ctor.isConstructor() || ctor.isInterface()); Preconditions.checkArgument(!ctor.isUnknownType()); // The type system should notice inheritance cycles on its own // and break the cycle. while (true) { ObjectType maybeSuperInstanceType = ctor.getPrototype().getImplicitPrototype(); if (maybeSuperInstanceType == null) { return false; } if (maybeSuperInstanceType.isUnknownType() || maybeSuperInstanceType.isEmptyType()) { return true; } ctor = maybeSuperInstanceType.getConstructor(); if (ctor == null) { return false; } Preconditions.checkState(ctor.isConstructor() || ctor.isInterface()); } } /** * Visits an ASSIGN node for cases such as * <pre> * interface.property2.property = ...; * </pre> */ private void visitInterfaceGetprop(NodeTraversal t, Node assign, Node object, String property, Node lvalue, Node rvalue) { JSType rvalueType = getJSType(rvalue); // Only 2 values are allowed for methods: // goog.abstractMethod // function () {}; // or for properties, no assignment such as: // InterfaceFoo.prototype.foobar; String abstractMethodName = compiler.getCodingConvention().getAbstractMethodName(); if (!rvalueType.isOrdinaryFunction() && !(rvalue.isQualifiedName() && rvalue.getQualifiedName().equals(abstractMethodName))) { // This is bad i18n style but we don't localize our compiler errors. String abstractMethodMessage = (abstractMethodName != null) ? ", or " + abstractMethodName : ""; compiler.report( t.makeError(object, INVALID_INTERFACE_MEMBER_DECLARATION, abstractMethodMessage)); } if (assign.getLastChild().isFunction() && !NodeUtil.isEmptyBlock(assign.getLastChild().getLastChild())) { compiler.report( t.makeError(object, INTERFACE_FUNCTION_NOT_EMPTY, abstractMethodName)); } } /** * Visits an ASSIGN node for cases such as * <pre> * object.property = ...; * </pre>

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>/* * Copyright 2010 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.jscomp.regex.RegExpTree; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.Node; /** * Look for references to the global RegExp object that would cause * regular expressions to be unoptimizable, and checks that regular expressions * are syntactically valid. * * @author johnlenz@google.com (John Lenz) */ class CheckRegExp extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType REGEXP_REFERENCE = DiagnosticType.warning("JSC_REGEXP_REFERENCE", "References to the global RegExp object prevents " + "optimization of regular expressions."); static final DiagnosticType MALFORMED_REGEXP = DiagnosticType.warning( "JSC_MALFORMED_REGEXP", "Malformed Regular Expression: {0}"); private final AbstractCompiler compiler; private boolean globalRegExpPropertiesUsed = false; public boolean isGlobalRegExpPropertiesUsed() { return globalRegExpPropertiesUsed; } public CheckRegExp(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isReferenceName(n)) { String name = n.getString(); if (name.equals("RegExp") && t

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> = new JsDocInfoParser( new JsDocTokenStream(comment.substring(numOpeningChars), lineno, position2charno(position) + numOpeningChars), node, irNode, config, errorReporter); jsdocParser.setFileLevelJsDocBuilder(fileLevelJsDocBuilder); jsdocParser.setFileOverviewJSDocInfo(fileOverviewInfo); jsdocParser.parse(); return jsdocParser; } // Set the length on the node if we're in IDE mode. private void maybeSetLengthFrom(Node node, AstNode source) { if (config.isIdeMode) { node.setLength(source.getLength()); } } private int position2charno(int position) { int lineIndex = sourceString.lastIndexOf('\n', position); if (lineIndex == -1) { return position; } else { // Subtract one for initial position being 0. return position - lineIndex - 1; } } private Node justTransform(AstNode node) { return transformDispatcher.process(node); } private class TransformDispatcher extends TypeSafeDispatcher<Node> { private Node processGeneric( com.google.javascript.jscomp.mozilla.rhino.Node n) { Node node = newNode(transformTokenType(n.getType())); for (com.google.javascript.jscomp.mozilla.rhino.Node child : n) { node.addChildToBack(transform((AstNode)child)); } return node; } /** * Transforms the given node and then sets its type to Token.STRING if it * was Token.NAME. If its type was already Token.STRING, then quotes it. * Used for properties, as the old AST uses String tokens, while the new one * uses Name tokens for unquoted strings. For example, in * var o = {'a' : 1, b: 2}; * the string 'a' is quoted, while the name b is turned into a string, but * unquoted. */ private Node transformAsString(AstNode n) { Node ret; if (n instanceof Name) { ret = transformNameAsString((Name)n); } else if (n instanceof NumberLiteral) { ret

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> string is // \v encoded, then all the vertical tabs in that string // will be encoded with a \v. int start = literalNode.getAbsolutePosition(); int end = start + literalNode.getLength(); if (start < sourceString.length() && (sourceString.substring( start, Math.min(sourceString.length(), end)) .indexOf("\\v") != -1)) { n.putBooleanProp(Node.SLASH_V, true); } } return n; } @Override Node processSwitchCase(SwitchCase caseNode) { Node node; if (caseNode.isDefault()) { node = newNode(Token.DEFAULT_CASE); } else { AstNode expr = caseNode.getExpression(); node = newNode(Token.CASE, transform(expr)); } Node block = newNode(Token.BLOCK); block.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); block.setLineno(caseNode.getLineno()); block.setCharno(position2charno(caseNode.getAbsolutePosition())); maybeSetLengthFrom(block, caseNode); if (caseNode.getStatements() != null) { for (AstNode child : caseNode.getStatements()) { block.addChildToBack(transform(child)); } } node.addChildToBack(block); return node; } @Override Node processSwitchStatement(SwitchStatement statementNode) { Node node = newNode(Token.SWITCH, transform(statementNode.getExpression())); for (AstNode child : statementNode.getCases()) { node.addChildToBack(transform(child)); } return node; } @Override Node processThrowStatement(ThrowStatement statementNode) { return newNode(Token.THROW, transform(statementNode.getExpression())); } @Override Node processTryStatement(TryStatement statementNode) { Node node = newNode(Token.TRY, transformBlock(statementNode.getTryBlock())); Node block = newNode(Token.BLOCK); boolean lineSet = false; for (CatchClause cc : statementNode.getCatchClauses()) { // Mark the enclosing block at the same line as the first catch // clause. if

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> // max length of the number display int numberLength = Integer.toString(region.getEndingLineNumber()) .length(); // formatting StringBuilder builder = new StringBuilder(code.length() * 2); int start = 0; int end = code.indexOf('\n', start); int lineNumber = region.getBeginningLineNumber(); while (start >= 0) { // line extraction String line; if (end < 0) { line = code.substring(start); if (line.length() == 0) { return builder.substring(0, builder.length() - 1); } } else { line = code.substring(start, end); } builder.append(" "); // nice spaces for the line number int spaces = numberLength - Integer.toString(lineNumber).length(); builder.append(Strings.repeat(" ", spaces)); builder.append(lineNumber); builder.append("| "); // end & update if (end < 0) { builder.append(line); start = -1; } else { builder.append(line); builder.append('\n'); start = end + 1; end = code.indexOf('\n', start); lineNumber++; } } return builder.toString(); } } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> a named function: {0}."); private final AbstractCompiler compiler; private final CheckLevel checkLevel; // NOTE(nicksantos): It's a lot faster to use a shared Set that // we clear after each method call, because the Set never gets too big. private final Set<BasicBlock> blocksWithDeclarations = Sets.newHashSet(); public VariableReferenceCheck(AbstractCompiler compiler, CheckLevel checkLevel) { this.compiler = compiler; this.checkLevel = checkLevel; } @Override public void process(Node externs, Node root) { ReferenceCollectingCallback callback = new ReferenceCollectingCallback( compiler, new ReferenceCheckingBehavior()); callback.process(externs, root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { ReferenceCollectingCallback callback = new ReferenceCollectingCallback( compiler, new ReferenceCheckingBehavior()); callback.hotSwapScript(scriptRoot, originalRoot); } /** * Behavior that checks variables for redeclaration or early references * just after they go out of scope. */ private class ReferenceCheckingBehavior implements Behavior { @Override public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) { // TODO(bashir) In hot-swap version this means that for global scope we // only go through all global variables accessed in the modified file not // all global variables. This should be fixed. // Check all vars after finishing a scope for (Iterator<Var> it = t.getScope().getVars(); it.hasNext();) { Var v = it.next(); checkVar(t, v, referenceMap.getReferences(v).references); } } /** * If the variable is declared more than once in a basic block, generate a * warning. Also check if a variable is used in a given scope before it is * declared, which suggest a likely error. Relies on the fact that * references is in parse-tree order. */ private void checkVar(NodeTraversal t, Var v, List<Reference> references) { blocksWithDeclarations.clear(); boolean isDeclaredInScope = false; boolean isUnhoistedNamedFunction = false; Reference hoistedFn = null; // Look for hoisted functions. for (Reference

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> reference : references) { if (reference.isHoistedFunction()) { blocksWithDeclarations.add(reference.getBasicBlock()); isDeclaredInScope = true; hoistedFn = reference; break; } else if (NodeUtil.isFunctionDeclaration( reference.getNode().getParent())) { isUnhoistedNamedFunction = true; } } for (Reference reference : references) { if (reference == hoistedFn) { continue; } BasicBlock basicBlock = reference.getBasicBlock(); boolean isDeclaration = reference.isDeclaration(); boolean allowDupe = SyntacticScopeCreator.hasDuplicateDeclarationSuppression( reference.getNode(), v); if (isDeclaration && !allowDupe) { // Look through all the declarations we've found so far, and // check if any of them are before this block. for (BasicBlock declaredBlock : blocksWithDeclarations) { if (declaredBlock.provablyExecutesBefore(basicBlock)) { // TODO(johnlenz): Fix AST generating clients that so they would // have property StaticSourceFile attached at each node. Or // better yet, make sure the generated code never violates // the requirement to pass aggressive var check! String filename = NodeUtil.getSourceName(reference.getNode()); compiler.report( JSError.make(filename, reference.getNode(), checkLevel, REDECLARED_VARIABLE, v.name)); break; } } } if (isUnhoistedNamedFunction && !isDeclaration && isDeclaredInScope) { // Only allow an unhoisted named function to be used within the // block it is declared. for (BasicBlock declaredBlock : blocksWithDeclarations) { if (!declaredBlock.provablyExecutesBefore(basicBlock)) { String filename = NodeUtil.getSourceName(reference.getNode()); compiler.report( JSError.make(filename, reference.getNode(), AMBIGUOUS_FUNCTION_DECL, v.name)); break; } } } if (!isDeclaration && !isDeclaredInScope) { // Don't check the order of refer in externs files. if (!reference.getNode().isFromExterns()) { // Special case to deal with var goog = goog || {} Node grandparent = reference

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>.getGrandparent(); if (grandparent.isName() && grandparent.getString() == v.name) { continue; } // Only generate warnings if the scopes do not match in order // to deal with possible forward declarations and recursion if (reference.getScope() == v.scope) { String filename = NodeUtil.getSourceName(reference.getNode()); compiler.report( JSError.make(filename, reference.getNode(), checkLevel, UNDECLARED_REFERENCE, v.name)); } } } if (isDeclaration) { blocksWithDeclarations.add(basicBlock); isDeclaredInScope = true; } } } } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>.getErrors(); } /** * Returns the array of warnings (never null). */ public JSError[] getWarnings() { return errorManager.getWarnings(); } @Override public Node getRoot() { return externAndJsRoot; } /** * Creates a new id for making unique names. */ private int nextUniqueNameId() { return uniqueNameId++; } /** * Resets the unique name id counter */ @VisibleForTesting void resetUniqueNameId() { uniqueNameId = 0; } @Override Supplier<String> getUniqueNameIdSupplier() { final Compiler self = this; return new Supplier<String>() { @Override public String get() { return String.valueOf(self.nextUniqueNameId()); } }; } @Override boolean areNodesEqualForInlining(Node n1, Node n2) { if (options.ambiguateProperties || options.disambiguateProperties) { // The type based optimizations require that type information is preserved // during other optimizations. return n1.isEquivalentToTyped(n2); } else { return n1.isEquivalentTo(n2); } } //------------------------------------------------------------------------ // Inputs //------------------------------------------------------------------------ // TODO(nicksantos): Decide which parts of these belong in an AbstractCompiler // interface, and which ones should always be injected. @Override public CompilerInput getInput(InputId id) { return inputsById.get(id); } /** * Removes an input file from AST. * @param id The id of the input to be removed. */ protected void removeExternInput(InputId id) { CompilerInput input = getInput(id); if (input == null) { return; } Preconditions.checkState(input.isExtern(), "Not an extern input: %s", input.getName()); inputsById.remove(id); externs.remove(input); Node root = input.getAstRoot(this); if (root != null) { root.detachFromParent(); } } @Override public CompilerInput newExternInput(String name) { SourceAst ast = new SyntheticAst(name); if

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> /** * Converts the parse tree for each input in a module back to js code. */ public String[] toSourceArray(final JSModule module) { return runInCompilerThread(new Callable<String[]>() { @Override public String[] call() throws Exception { List<CompilerInput> inputs = module.getInputs(); int numInputs = inputs.size(); if (numInputs == 0) { return new String[0]; } String[] sources = new String[numInputs]; CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); if (scriptNode == null) { throw new IllegalArgumentException( "Bad module input: " + inputs.get(i).getName()); } cb.reset(); toSource(cb, i, scriptNode); sources[i] = cb.toString(); } return sources; } }); } /** * Writes out js code from a root node. If printing input delimiters, this * method will attach a comment to the start of the text indicating which * input the output derived from. If there were any preserve annotations * within the root's source, they will also be printed in a block comment * at the beginning of the output. */ public void toSource(final CodeBuilder cb, final int inputSeqNum, final Node root) { runInCompilerThread(new Callable<Void>() { @Override public Void call() throws Exception { if (options.printInputDelimiter) { if ((cb.getLength() > 0) && !cb.endsWith("\n")) { cb.append("\n"); // Make sure that the label starts on a new line } Preconditions.checkState(root.isScript()); String delimiter = options.inputDelimiter; String inputName = root.getInputId().getIdName(); String sourceName = root.getSourceFileName(); Preconditions.checkState(sourceName != null); Preconditions.checkState(!sourceName.isEmpty()); delimiter = delimiter .replaceAll("%name%", Matcher.quoteReplacement(inputName)) .replaceAll("%num%", String.valueOf(inputSeqNum)); cb.append(delimiter) .

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>append("\n"); } if (root.getJSDocInfo() != null && root.getJSDocInfo().getLicense() != null) { cb.append("/*\n") .append(root.getJSDocInfo().getLicense()) .append("*/\n"); } // If there is a valid source map, then indicate to it that the current // root node's mappings are offset by the given string builder buffer. if (options.sourceMapOutputPath != null) { sourceMap.setStartingPosition( cb.getLineIndex(), cb.getColumnIndex()); } // if LanguageMode is ECMASCRIPT5_STRICT, only print 'use strict' // for the first input file String code = toSource(root, sourceMap, inputSeqNum == 0); if (!code.isEmpty()) { cb.append(code); // In order to avoid parse ambiguity when files are concatenated // together, all files should end in a semi-colon. Do a quick // heuristic check if there's an obvious semi-colon already there. int length = code.length(); char lastChar = code.charAt(length - 1); char secondLastChar = length >= 2 ? code.charAt(length - 2) : '\0'; boolean hasSemiColon = lastChar == ';' || (lastChar == '\n' && secondLastChar == ';'); if (!hasSemiColon) { cb.append(";"); } } return null; } }); } /** * Generates JavaScript source code for an AST, doesn't generate source * map info. */ @Override String toSource(Node n) { initCompilerOptionsIfTesting(); return toSource(n, null, true); } /** * Generates JavaScript source code for an AST. */ private String toSource(Node n, SourceMap sourceMap, boolean firstOutput) { CodePrinter.Builder builder = new CodePrinter.Builder(n); builder.setPrettyPrint(options.prettyPrint); builder.setLineBreak(options.lineBreak); builder.setSourceMap(sourceMap); builder.setSourceMapDetailLevel(options.sourceMapDetailLevel); builder.setTagAsStrict(firstOutput &&

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> options.getLanguageOut() == LanguageMode.ECMASCRIPT5_STRICT); builder.setLineLengthThreshold(options.lineLengthThreshold); Charset charset = options.outputCharset != null ? Charset.forName(options.outputCharset) : null; builder.setOutputCharset(charset); return builder.build(); } /** * Stores a buffer of text to which more can be appended. This is just like a * StringBuilder except that we also track the number of lines. */ public static class CodeBuilder { private final StringBuilder sb = new StringBuilder(); private int lineCount = 0; private int colCount = 0; /** Removes all text, but leaves the line count unchanged. */ void reset() { sb.setLength(0); } /** Appends the given string to the text buffer. */ CodeBuilder append(String str) { sb.append(str); // Adjust the line and column information for the new text. int index = -1; int lastIndex = index; while ((index = str.indexOf('\n', index + 1)) >= 0) { ++lineCount; lastIndex = index; } if (lastIndex == -1) { // No new lines, append the new characters added. colCount += str.length(); } else { colCount = str.length() - (lastIndex + 1); } return this; } /** Returns all text in the text buffer. */ @Override public String toString() { return sb.toString(); } /** Returns the length of the text buffer. */ public int getLength() { return sb.length(); } /** Returns the (zero-based) index of the last line in the text buffer. */ int getLineIndex() { return lineCount; } /** Returns the (zero-based) index of the last column in the text buffer. */ int getColumnIndex() { return colCount; } /** Determines whether the text ends with the given suffix. */ boolean endsWith(String suffix) { return (sb.length() > suffix.length()) && suffix.equals(sb.substring(sb.length() - suffix.length())); } } //------------------------------------------------------------------------ // Optimizations

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> //------------------------------------------------------------------------ public void optimize() { // Ideally, this pass should be the first pass run, however: // 1) VariableReferenceCheck reports unexpected warnings if Normalize // is done first. // 2) ReplaceMessages, stripCode, and potentially custom passes rely on // unmodified local names. normalize(); PhaseOptimizer phaseOptimizer = new PhaseOptimizer(this, tracker); if (options.devMode == DevMode.EVERY_PASS) { phaseOptimizer.setSanityCheck(sanityCheck); } phaseOptimizer.consume(getPassConfig().getOptimizations()); phaseOptimizer.process(externsRoot, jsRoot); if (hasErrors()) { return; } } @Override void setCssRenamingMap(CssRenamingMap map) { options.cssRenamingMap = map; } @Override CssRenamingMap getCssRenamingMap() { return options.cssRenamingMap; } /** * Reprocesses the current defines over the AST. This is used by GwtCompiler * to generate N outputs for different targets from the same (checked) AST. * For each target, we apply the target-specific defines by calling * {@code processDefines} and then {@code optimize} to optimize the AST * specifically for that target. */ public void processDefines() { (new DefaultPassConfig(options)).processDefines.create(this) .process(externsRoot, jsRoot); } boolean isInliningForbidden() { return options.propertyRenaming == PropertyRenamingPolicy.HEURISTIC || options.propertyRenaming == PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC; } /** Control Flow Analysis. */ ControlFlowGraph<Node> computeCFG() { logger.info("Computing Control Flow Graph"); Tracer tracer = newTracer("computeCFG"); ControlFlowAnalysis cfa = new ControlFlowAnalysis(this, true, false); process(cfa); stopTracer(tracer, "computeCFG"); return cfa.getCfg(); } public void normalize() { logger.info("Normalizing"); startPass("normalize"); process(new Normalize(this, false)); endPass(); } @Override void prepareAst(Node root) { Compiler

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>Pass pass = new PrepareAst(this); pass.process(null, root); } void recordFunctionInformation() { logger.info("Recording function information"); startPass("recordFunctionInformation"); RecordFunctionInformation recordFunctionInfoPass = new RecordFunctionInformation( this, getPassConfig().getIntermediateState().functionNames); process(recordFunctionInfoPass); functionInformationMap = recordFunctionInfoPass.getMap(); endPass(); } protected final CodeChangeHandler.RecentChange recentChange = new CodeChangeHandler.RecentChange(); private final List<CodeChangeHandler> codeChangeHandlers = Lists.<CodeChangeHandler>newArrayList(); @Override void addChangeHandler(CodeChangeHandler handler) { codeChangeHandlers.add(handler); } @Override void removeChangeHandler(CodeChangeHandler handler) { codeChangeHandlers.remove(handler); } /** * All passes should call reportCodeChange() when they alter * the JS tree structure. This is verified by CompilerTestCase. * This allows us to optimize to a fixed point. */ @Override public void reportCodeChange() { for (CodeChangeHandler handler : codeChangeHandlers) { handler.reportChange(); } } @Override public CodingConvention getCodingConvention() { CodingConvention convention = options.getCodingConvention(); convention = convention != null ? convention : defaultCodingConvention; return convention; } @Override public boolean isIdeMode() { return options.ideMode; } @Override public boolean acceptEcmaScript5() { switch (options.getLanguageIn()) { case ECMASCRIPT5: case ECMASCRIPT5_STRICT: return true; } return false; } public LanguageMode languageMode() { return options.getLanguageIn(); } @Override public boolean acceptConstKeyword() { return options.acceptConstKeyword; } @Override Config getParserConfig() { if (parserConfig == null) { Config.LanguageMode mode; switch (options.getLanguageIn()) { case ECMASCRIPT3: mode = Config.LanguageMode.ECMASCRIPT3; break; case ECMASCRIPT5: mode = Config.LanguageMode.ECMASCRIPT5;

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.parsing.ParserRunner; import com.google.javascript.rhino.IR; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import java.io.IOException; import java.util.logging.Logger; /** * Generates an AST for a JavaScript source file. * */ public class JsAst implements SourceAst { private static final Logger logger_ = Logger.getLogger(JsAst.class.getName()); private static final long serialVersionUID = 1L; private transient InputId inputId; private transient SourceFile sourceFile; private String fileName; private Node root; public JsAst(SourceFile sourceFile) { this.inputId = new InputId(sourceFile.getName()); this.sourceFile = sourceFile; this.fileName = sourceFile.getName(); } @Override public Node getAstRoot(AbstractCompiler compiler) { if (root == null) { parse(compiler); root.setInputId(inputId); } return root; } @Override public void clearAst() { root = null; // While we're at it, clear out any saved text in the source file on // the assumption that if we're dumping the parse tree, then we probably // assume regenerating everything else is a smart idea also. sourceFile.clearCachedSource(); } @Override public InputId getInputId() { return inputId;

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> } @Override public SourceFile getSourceFile() { return sourceFile; } @Override public void setSourceFile(SourceFile file) { Preconditions.checkState(fileName.equals(file.getName())); sourceFile = file; } private void parse(AbstractCompiler compiler) { try { logger_.fine("Parsing: " + sourceFile.getName()); root = ParserRunner.parse(sourceFile, sourceFile.getCode(), compiler.getParserConfig(), compiler.getDefaultErrorReporter(), logger_); } catch (IOException e) { compiler.report( JSError.make(AbstractCompiler.READ_ERROR, sourceFile.getName())); } if (root == null || compiler.hasHaltingErrors()) { // There was a parse error or IOException, so use a dummy block. root = IR.script(); } else { compiler.prepareAst(root); } // Set the source name so that the compiler passes can track // the source file and module. root.setStaticSourceFile(sourceFile); } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>rhino.Node; import com.google.javascript.rhino.jstype.StaticReference; import com.google.javascript.rhino.jstype.StaticSlot; import java.io.Serializable; import java.util.Set; /** * Object type. * * In JavaScript, all object types have properties, and each of those * properties has a type. Property types may be DECLARED, INFERRED, or * UNKNOWN. * * DECLARED properties have an explicit type annotation, as in: * <code> * /xx @type {number} x/ * Foo.prototype.bar = 1; * </code> * This property may only hold number values, and an assignment to any * other type of value is an error. * * INFERRED properties do not have an explicit type annotation. Rather, * we try to find all the possible types that this property can hold. * <code> * Foo.prototype.bar = 1; * </code> * If the programmer assigns other types of values to this property, * the property will take on the union of all these types. * * UNKNOWN properties are properties on the UNKNOWN type. The UNKNOWN * type has all properties, but we do not know whether they are * declared or inferred. * */ public abstract class ObjectType extends JSType implements StaticScope<JSType> { private boolean visited; private JSDocInfo docInfo = null; private boolean unknown = true; ObjectType(JSTypeRegistry registry) { super(registry); } @Override public Node getRootNode() { return null; } @Override public ObjectType getParentScope() { return getImplicitPrototype(); } @Override public abstract Property getSlot(String name); @Override public Property getOwnSlot(String name) { if (hasOwnProperty(name)) { return getSlot(name); } return null; } @Override public ObjectType getTypeOfThis() { return null; } /** * Gets the declared default element type. * @see ParameterizedType */ public JSType getParameterType() { return null; } /** * Gets the declared default index type. * @see IndexedType */ public

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> name does not have these suffixes, and as such, * recollapses these implicit types back to their real type. */ public String getNormalizedReferenceName() { String name = getReferenceName(); if (name != null) { int pos = name.indexOf("("); if (pos != -1) { return name.substring(0, pos); } } return name; } @Override public String getDisplayName() { return getNormalizedReferenceName(); } /** * Creates a suffix for a proxy delegate. * @see #getNormalizedReferenceName */ public static String createDelegateSuffix(String suffix) { return "(" + suffix + ")"; } /** * Returns true if the object is named. * @return true if the object is named, false if it is anonymous */ public boolean hasReferenceName() { return false; } @Override public TernaryValue testForEquality(JSType that) { // super TernaryValue result = super.testForEquality(that); if (result != null) { return result; } // objects are comparable to everything but null/undefined if (that.isSubtype( getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) { return UNKNOWN; } else { return FALSE; } } /** * Gets this object's constructor. * @return this object's constructor or {@code null} if it is a native * object (constructed natively v.s. by instantiation of a function) */ public abstract FunctionType getConstructor(); /** * Gets the implicit prototype (a.k.a. the {@code [[Prototype]]} property). */ public abstract ObjectType getImplicitPrototype(); /** * Defines a property whose type is synthesized (i.e. not inferred). * @param propertyName the property's name * @param type the type * @param propertyNode the node corresponding to the declaration of property * which might later be accessed using {@code getPropertyNode}. */ public final boolean defineDeclaredProperty(String propertyName, JSType type, Node propertyNode) { boolean result = defineProperty(propertyName, type, false, propertyNode); // All property definitions go through this method // or define

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>/* * Copyright 2011 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.InputId; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * This class walks the AST and validates that the structure is correct. * * @author johnlenz@google.com (John Lenz) */ public class AstValidator implements CompilerPass { // Possible enhancements: // * verify NAME, LABEL_NAME, GETPROP property name and unquoted // object-literal keys are valid javascript identifiers. // * optionally verify every node has source location information. // * optionally verify every node has an assigned JSType // public interface ViolationHandler { void handleViolation(String message, Node n); } private final ViolationHandler violationHandler; public AstValidator(ViolationHandler handler) { this.violationHandler = handler; } public AstValidator() { this.violationHandler = new ViolationHandler() { @Override public void handleViolation(String message, Node n) { throw new IllegalStateException( message + " Reference node " + n.toString()); } }; } @Override public void process(Node externs, Node root) { if (externs != null) { validateCodeRoot(externs); } if (root != null) { validateCodeRoot(root); } } public void validateRoot(Node n) { validateNodeType(Token.BLOCK, n); validateIsSynthetic(n); validateChildCount(n, 2); validateCodeRoot

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> return; case Token.FUNCTION: if (NodeUtil.isFunctionExpression(n)) { return; } String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(n.getFirstChild()); return; // should not examine function's children case Token.CATCH: Preconditions.checkState(n.getChildCount() == 2); Preconditions.checkState(n.getFirstChild().isName()); // the first child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext(); declareVar(var); scanVars(block, n); return; // only one child to scan case Token.SCRIPT: inputId = n.getInputId(); Preconditions.checkNotNull(inputId); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child, n); child = next; } } } /** * Interface for injectable duplicate handling. */ interface RedeclarationHandler { void onRedeclaration( Scope s, String name, Node n, CompilerInput input); } /** * The default handler for duplicate declarations. */ private class DefaultRedeclarationHandler implements RedeclarationHandler { @Override public void onRedeclaration( Scope s, String name, Node n, CompilerInput input) { Node parent = n.getParent(); // Don't allow multiple variables to be declared at the top level scope if (scope.isGlobal()) { Scope.Var origVar = scope.getVar(name); Node origParent = origVar.getParentNode(); if (origParent.isCatch() && parent.isCatch()) { // Okay, both are 'catch(x)' variables. return; } boolean allowDupe = hasDuplicateDeclarationSuppression(n, origVar); if (!allow

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>/* * Copyright 2011 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.DiagnosticType; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Node; /** * {@link CheckDebuggerStatement} checks for the presence of the "debugger" * statement in JavaScript code. It is appropriate to use this statement while * developing JavaScript; however, it is generally undesirable to include it in * production code. * * @author bolinfest@google.com (Michael Bolin) */ class CheckDebuggerStatement extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType DEBUGGER_STATEMENT_PRESENT = DiagnosticType.disabled("JSC_DEBUGGER_STATEMENT_PRESENT", "Using the debugger statement can halt your application if the user " + "has a JavaScript debugger running."); private final AbstractCompiler compiler; public CheckDebuggerStatement(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isDebugger()) { t.report(n, DEBUGGER_STATEMENT_PRESENT); } } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> final DiagnosticType ARGUMENTS_DECLARATION = DiagnosticType.warning( "JSC_ARGUMENTS_DECLARATION", "\"arguments\" cannot be redeclared in ES5 strict mode"); static final DiagnosticType ARGUMENTS_ASSIGNMENT = DiagnosticType.warning( "JSC_ARGUMENTS_ASSIGNMENT", "the \"arguments\" object cannot be reassigned in ES5 strict mode"); static final DiagnosticType DELETE_VARIABLE = DiagnosticType.warning( "JSC_DELETE_VARIABLE", "variables, functions, and arguments cannot be deleted in " + "ES5 strict mode"); static final DiagnosticType ILLEGAL_NAME = DiagnosticType.error( "JSC_ILLEGAL_NAME", "identifiers ending in '__' cannot be used in Caja"); static final DiagnosticType DUPLICATE_OBJECT_KEY = DiagnosticType.warning( "JSC_DUPLICATE_OBJECT_KEY", "object literals cannot contain duplicate keys in ES5 strict mode"); private final AbstractCompiler compiler; private final boolean noVarCheck; private final boolean noCajaChecks; StrictModeCheck(AbstractCompiler compiler) { this(compiler, false, false); } StrictModeCheck( AbstractCompiler compiler, boolean noVarCheck, boolean noCajaChecks) { this.compiler = compiler; this.noVarCheck = noVarCheck; this.noCajaChecks = noCajaChecks; } @Override public void process(Node externs, Node root) { NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); NodeTraversal.traverse(compiler, root, new NonExternChecks()); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { if (!isDeclaration(n)) { checkNameUse(t, n); } } else if (n.isAssign()) { checkAssignment(t, n); } else if (n.isDelProp()) { checkDelete(t, n); } else if (n.isObjectLit()) { checkObjectLiteral(t, n); } else if (n.isLabel()) { checkLabel(t, n); } } /** * Determines if the given name

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> is a declaration, which can be a declaration * of a variable, function, or argument. */ private static boolean isDeclaration(Node n) { switch (n.getParent().getType()) { case Token.VAR: case Token.FUNCTION: case Token.CATCH: return true; case Token.PARAM_LIST: return n.getParent().getParent().isFunction(); default: return false; } } /** Checks that the given name is used legally. */ private void checkNameUse(NodeTraversal t, Node n) { Var v = t.getScope().getVar(n.getString()); if (v == null) { // In particular, this prevents creating a global variable by assigning // to it without a declaration. if (!noVarCheck) { t.report(n, UNKNOWN_VARIABLE, n.getString()); } } if (!noCajaChecks) { if ("eval".equals(n.getString())) { t.report(n, EVAL_USE); } else if (n.getString().endsWith("__")) { t.report(n, ILLEGAL_NAME); } } } /** Checks that an assignment is not to the "arguments" object. */ private void checkAssignment(NodeTraversal t, Node n) { if (n.getFirstChild().isName()) { if ("arguments".equals(n.getFirstChild().getString())) { t.report(n, ARGUMENTS_ASSIGNMENT); } else if ("eval".equals(n.getFirstChild().getString())) { // Note that assignment to eval is already illegal because any use of // that name is illegal. if (noCajaChecks) { t.report(n, EVAL_ASSIGNMENT); } } } } /** Checks that variables, functions, and arguments are not deleted. */ private void checkDelete(NodeTraversal t, Node n) { if (n.getFirstChild().isName()) { Var v = t.getScope().getVar(n.getFirstChild().getString()); if (v != null) { t.report(n, DELETE_VARIABLE); } } } /** Checks that object literal keys are valid. */ private void checkObjectLiteral(NodeTraversal t, Node n) { Set

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS><String> getters = Sets.newHashSet(); Set<String> setters = Sets.newHashSet(); for (Node key = n.getFirstChild(); key != null; key = key.getNext()) { if (!noCajaChecks && key.getString().endsWith("__")) { t.report(key, ILLEGAL_NAME); } if (!key.isSetterDef()) { // normal property and getter cases if (getters.contains(key.getString())) { t.report(key, DUPLICATE_OBJECT_KEY); } else { getters.add(key.getString()); } } if (!key.isGetterDef()) { // normal property and setter cases if (setters.contains(key.getString())) { t.report(key, DUPLICATE_OBJECT_KEY); } else { setters.add(key.getString()); } } } } /** Checks that label names are valid. */ private void checkLabel(NodeTraversal t, Node n) { if (n.getFirstChild().getString().endsWith("__")) { if (!noCajaChecks) { t.report(n.getFirstChild(), ILLEGAL_NAME); } } } /** Checks that are performed on non-extern code only. */ private class NonExternChecks extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if ((n.isName()) && isDeclaration(n)) { checkDeclaration(t, n); } else if (n.isGetProp()) { checkProperty(t, n); } } /** Checks for illegal declarations. */ private void checkDeclaration(NodeTraversal t, Node n) { if ("eval".equals(n.getString())) { t.report(n, EVAL_DECLARATION); } else if ("arguments".equals(n.getString())) { t.report(n, ARGUMENTS_DECLARATION); } else if (n.getString().endsWith("__")) { if (!noCajaChecks) { t.report(n, ILLEGAL_NAME); } } } /** Checks for illegal property accesses. */ private void checkProperty(NodeTraversal t, Node n

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>) { if (n.getLastChild().getString().endsWith("__")) { if (!noCajaChecks) { t.report(n.getLastChild(), ILLEGAL_NAME); } } } } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> private final JsMessage.IdGenerator idGenerator; final AbstractCompiler compiler; /** * The names encountered associated with their defining node and source. We * use it for tracking duplicated message ids in the source code. */ private final Map<String, MessageLocation> messageNames = Maps.newHashMap(); /** * List of found goog.getMsg call nodes. * * When we visit goog.getMsg() node we add a pair node:sourcename and later * when we visit its parent we remove this pair. All nodes that are left at * the end of traversing are orphaned nodes. It means have no corresponding * var or property node. */ private final Map<Node, String> googMsgNodes = Maps.newHashMap(); private final CheckLevel checkLevel; /** * Creates js message visitor. * * @param compiler the compiler instance * @param needToCheckDuplications whether to check duplicated messages in * traversed * @param style style that should be used during parsing * @param idGenerator generator that used for creating unique ID for the * message */ JsMessageVisitor(AbstractCompiler compiler, boolean needToCheckDuplications, JsMessage.Style style, JsMessage.IdGenerator idGenerator) { this.compiler = compiler; this.needToCheckDuplications = needToCheckDuplications; this.style = style; this.idGenerator = idGenerator; checkLevel = (style == JsMessage.Style.CLOSURE) ? CheckLevel.ERROR : CheckLevel.WARNING; // TODO(anatol): add flag that decides whether to process UNNAMED messages. // Some projects would not want such functionality (unnamed) as they dont // use SOY templates. } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); for (Map.Entry<Node, String> msgNode : googMsgNodes.entrySet()) { compiler.report(JSError.make(msgNode.getValue(), msgNode.getKey(), checkLevel, MESSAGE_NODE_IS_ORPHANED)); } } @Override public void visit(NodeTraversal traversal, Node node, Node parent) { String messageKey; boolean isVar;

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>Node} does not * correspond to a valid js message node */ private void extractMessageFromProperty( Builder builder, Node getPropNode, Node assignNode) throws MalformedException { Node callNode = getPropNode.getNext(); maybeInitMetaDataFromJsDoc(builder, assignNode); extractFromCallNode(builder, callNode); } /** * Initializes the meta data in a JsMessage by examining the nodes just before * and after a message VAR node. * * @param builder the message builder whose meta data will be initialized * @param varNode the message VAR node * @param parentOfVarNode {@code varNode}'s parent node */ private void maybeInitMetaDataFromJsDocOrHelpVar( Builder builder, Node varNode, @Nullable Node parentOfVarNode) throws MalformedException { // First check description in @desc if (maybeInitMetaDataFromJsDoc(builder, varNode)) { return; } // Check the preceding node for meta data if ((parentOfVarNode != null) && maybeInitMetaDataFromHelpVar(builder, parentOfVarNode.getChildBefore(varNode))) { return; } // Check the subsequent node for meta data maybeInitMetaDataFromHelpVar(builder, varNode.getNext()); } /** * Initializes the meta data in a JsMessage by examining a node just before or * after a message VAR node. * * @param builder the message builder whose meta data will be initialized * @param sibling a node adjacent to the message VAR node * @return true iff message has corresponding description variable */ private boolean maybeInitMetaDataFromHelpVar(Builder builder, @Nullable Node sibling) throws MalformedException { if ((sibling != null) && (sibling.isVar())) { Node nameNode = sibling.getFirstChild(); String name = nameNode.getString(); if (name.equals(builder.getKey() + DESC_SUFFIX)) { Node valueNode = nameNode.getFirstChild(); String desc = extractStringFromStringExprNode(valueNode); if (desc.startsWith(HIDDEN_DESC_PREFIX)) { builder.setDesc(desc.substring(HIDDEN_DESC_PREFIX.length()).trim()); builder.setIsHidden(true); } else { builder

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> * an unregistered placeholder */ private void parseMessageTextNode(Builder builder, Node node) throws MalformedException { String value = extractStringFromStringExprNode(node); while(true) { int phBegin = value.indexOf(PH_JS_PREFIX); if (phBegin < 0) { // Just a string literal builder.appendStringPart(value); return; } else { if (phBegin > 0) { // A string literal followed by a placeholder builder.appendStringPart(value.substring(0, phBegin)); } // A placeholder. Find where it ends int phEnd = value.indexOf(PH_JS_SUFFIX, phBegin); if (phEnd < 0) { throw new MalformedException( "Placeholder incorrectly formatted in: " + builder.getKey(), node); } String phName = value.substring(phBegin + PH_JS_PREFIX.length(), phEnd); builder.appendPlaceholderReference(phName); int nextPos = phEnd + PH_JS_SUFFIX.length(); if (nextPos < value.length()) { // Iterate on the rest of the message value value = value.substring(nextPos); } else { // The message is parsed return; } } } } /** * Processes found js message. Several examples of "standard" processing * routines are: * <ol> * <li>extract all js messages * <li>replace js messages with localized versions for some specific language * <li>check that messages have correct syntax and present in localization * bundle * </ol> * * @param message the found message * @param definition the definition of the object and usually contains all * additional message information like message node/parent's node */ abstract void processJsMessage(JsMessage message, JsMessageDefinition definition); /** * Returns whether the given js identifier is a valid js message name. */ boolean isMessageName(String identifier, boolean isNewStyleMessage) { return identifier.startsWith(MSG_PREFIX) && (style == JsMessage.Style.CLOSURE || isNewStyleMessage || !identifier.endsWith(DESC_SUFFIX)); } /** * Returns whether the

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> given message name is in the unnamed namespace. */ private static boolean isUnnamedMessageName(String identifier) { return MSG_UNNAMED_PATTERN.matcher(identifier).matches(); } /** * Returns whether a string is nonempty, begins with a lowercase letter, and * contains only digits and underscores after the first underscore. */ static boolean isLowerCamelCaseWithNumericSuffixes(String input) { return CAMELCASE_PATTERN.matcher(input).matches(); } /** * Returns human-readable name of the given node's type. */ private static String getReadableTokenName(Node node) { return Token.name(node.getType()); } /** * Converts the given string from upper-underscore case to lower-camel case, * preserving numeric suffixes. For example: "NAME" -> "name" "A4_LETTER" -> * "a4Letter" "START_SPAN_1_23" -> "startSpan_1_23". */ static String toLowerCamelCaseWithNumericSuffixes(String input) { // Determine where the numeric suffixes begin int suffixStart = input.length(); while (suffixStart > 0) { char ch = '\0'; int numberStart = suffixStart; while (numberStart > 0) { ch = input.charAt(numberStart - 1); if (Character.isDigit(ch)) { numberStart--; } else { break; } } if ((numberStart > 0) && (numberStart < suffixStart) && (ch == '_')) { suffixStart = numberStart - 1; } else { break; } } if (suffixStart == input.length()) { return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, input); } else { return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, input.substring(0, suffixStart)) + input.substring(suffixStart); } } static class MalformedException extends Exception { private static final long serialVersionUID = 1L; private final Node node; MalformedException(String message, Node node) { super(message); this.node =

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> LinkedHashMultimap.create(); } else { this.invalidationMap = null; } } @Override public void process(Node externs, Node root) { for (TypeMismatch mis : compiler.getTypeValidator().getMismatches()) { addInvalidatingType(mis.typeA); addInvalidatingType(mis.typeB); recordInvalidationError(mis.typeA, mis.src); recordInvalidationError(mis.typeB, mis.src); } StaticScope<T> scope = typeSystem.getRootScope(); NodeTraversal.traverse(compiler, externs, new FindExternProperties()); NodeTraversal.traverse(compiler, root, new FindRenameableProperties()); renameProperties(); } private void recordInvalidationError(JSType t, JSError error) { if (!t.isObject()) { return; } if (t.isUnionType()) { for (JSType alt : t.toMaybeUnionType().getAlternates()) { recordInvalidationError(alt, error); } return; } if (invalidationMap != null) { invalidationMap.put(t, error); } } /** * Invalidates the given type, so that no properties on it will be renamed. */ private void addInvalidatingType(JSType type) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { for (JSType alt : type.toMaybeUnionType().getAlternates()) { addInvalidatingType(alt); } } else if (type.isEnumElementType()) { addInvalidatingType(type.toMaybeEnumElementType().getPrimitiveType()); } else { typeSystem.addInvalidatingType(type); ObjectType objType = ObjectType.cast(type); if (objType != null && objType.getImplicitPrototype() != null) { typeSystem.addInvalidatingType(objType.getImplicitPrototype()); } } } /** Returns the property for the given name, creating it if necessary. */ protected Property getProperty(String name) { if (!properties.containsKey(name)) { properties.put(name, new Property(name)); } return properties.get(name); } /** Public for testing

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * Check for usage of 'with'. * */ class ControlStructureCheck implements HotSwapCompilerPass { private final AbstractCompiler compiler; static final DiagnosticType USE_OF_WITH = DiagnosticType.warning( "JSC_USE_OF_WITH", "The use of the 'with' structure should be avoided."); ControlStructureCheck(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { check(root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { check(scriptRoot); } /** * Reports errors for any invalid use of control structures. * * @param node Current node to check. */ private void check(Node node) { switch (node.getType()) { case Token.WITH: JSDocInfo info = node.getJSDocInfo(); boolean allowWith = info != null && info.getSuppressions().contains("with"); if (!allowWith) { report(node, USE_OF_WITH); } break; } for (Node bChild = node.getFirstChild(); bChild != null;) { Node next = bChild.getNext(); check(bChild); bChild = next; } } private void report(Node n, Diagnostic

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> {0} assigned a value more than once"); static final DiagnosticType CONST_PROPERTY_DELETED = DiagnosticType.warning( "JSC_CONSTANT_PROPERTY_DELETED", "constant property {0} cannot be deleted"); private final AbstractCompiler compiler; private final TypeValidator validator; // State about the current traversal. private int deprecatedDepth = 0; private int methodDepth = 0; private JSType currentClass = null; private final Multimap<String, String> initializedConstantProperties; CheckAccessControls(AbstractCompiler compiler) { this.compiler = compiler; this.validator = compiler.getTypeValidator(); this.initializedConstantProperties = HashMultimap.create(); } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { NodeTraversal.traverse(compiler, scriptRoot, this); } @Override public void enterScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); Node parent = n.getParent(); if (isDeprecatedFunction(n, parent)) { deprecatedDepth++; } if (methodDepth == 0) { currentClass = getClassOfMethod(n, parent); } methodDepth++; } } @Override public void exitScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); Node parent = n.getParent(); if (isDeprecatedFunction(n, parent)) { deprecatedDepth--; } methodDepth--; if (methodDepth == 0) { currentClass = null; } } } /** * Gets the type of the class that "owns" a method, or null if * we know that its un-owned. */ private JSType getClassOfMethod(Node n, Node parent) { if (parent.isAssign()) { Node lValue = parent.getFirstChild(); if (NodeUtil.isGet(lValue)) { // We have an assignment of the form "a.b = ...". JSType lValueType = lValue.getJSType(); if (lValueType

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> != null && lValueType.isNominalConstructor()) { // If a.b is a constructor, then everything in this function // belongs to the "a.b" type. return (lValueType.toMaybeFunctionType()).getInstanceType(); } else { // If a.b is not a constructor, then treat this as a method // of whatever type is on "a". return normalizeClassType(lValue.getFirstChild().getJSType()); } } else { // We have an assignment of the form "a = ...", so pull the // type off the "a". return normalizeClassType(lValue.getJSType()); } } else if (NodeUtil.isFunctionDeclaration(n) || parent.isName()) { return normalizeClassType(n.getJSType()); } return null; } /** * Normalize the type of a constructor, its instance, and its prototype * all down to the same type (the instance type). */ private JSType normalizeClassType(JSType type) { if (type == null || type.isUnknownType()) { return type; } else if (type.isNominalConstructor()) { return (type.toMaybeFunctionType()).getInstanceType(); } else if (type.isFunctionPrototypeType()) { FunctionType owner = ((ObjectType) type).getOwnerFunction(); if (owner.isConstructor()) { return owner.getInstanceType(); } } return type; } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: checkNameDeprecation(t, n, parent); checkNameVisibility(t, n, parent); break; case Token.GETPROP: checkPropertyDeprecation(t, n, parent); checkPropertyVisibility(t, n, parent); checkConstantProperty(t, n); break; case Token.NEW: checkConstructorDeprecation(t, n, parent); break; } } /** * Checks the given NEW node to ensure that access restrictions are obeyed. */ private void checkConstructorDeprecation(NodeTraversal t, Node n,

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>.idGeneratorMap; } GlobalNamespace getGlobalNamespace() { return namespaceForChecks; } PreprocessorSymbolTable getPreprocessorSymbolTable() { return preprocessorSymbolTable; } void maybeInitializePreprocessorSymbolTable(AbstractCompiler compiler) { if (options.ideMode) { Node root = compiler.getRoot(); if (preprocessorSymbolTable == null || preprocessorSymbolTable.getRootNode() != root) { preprocessorSymbolTable = new PreprocessorSymbolTable(root); } } } @Override protected List<PassFactory> getChecks() { List<PassFactory> checks = Lists.newArrayList(); if (options.closurePass) { checks.add(closureGoogScopeAliases); } if (options.nameAnonymousFunctionsOnly) { if (options.anonymousFunctionNaming == AnonymousFunctionNamingPolicy.MAPPED) { checks.add(nameMappedAnonymousFunctions); } else if (options.anonymousFunctionNaming == AnonymousFunctionNamingPolicy.UNMAPPED) { checks.add(nameUnmappedAnonymousFunctions); } return checks; } if (options.checkSuspiciousCode || options.enables(DiagnosticGroups.GLOBAL_THIS) || options.enables(DiagnosticGroups.DEBUGGER_STATEMENT_PRESENT)) { checks.add(suspiciousCode); } if (options.checkControlStructures || options.enables(DiagnosticGroups.ES5_STRICT)) { checks.add(checkControlStructures); } if (options.checkRequires.isOn()) { checks.add(checkRequires); } if (options.checkProvides.isOn()) { checks.add(checkProvides); } // The following passes are more like "preprocessor" passes. // It's important that they run before most checking passes. // Perhaps this method should be renamed? if (options.generateExports) { checks.add(generateExports); } if (options.exportTestFunctions) { checks.add(exportTestFunctions); } if (options.closurePass) { checks.add(closurePrimitives.makeOneTimePass()); } if (options.closurePass && options.checkMissingGetCssNameLevel.isOn()) { checks.add(closureCheckGet

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> return new CheckRequiresForConstructors(compiler, options.checkRequires); } }; /** Makes sure @constructor is paired with goog.provides(). */ final HotSwapPassFactory checkProvides = new HotSwapPassFactory("checkProvides", true) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { return new CheckProvides(compiler, options.checkProvides); } }; private static final DiagnosticType GENERATE_EXPORTS_ERROR = DiagnosticType.error( "JSC_GENERATE_EXPORTS_ERROR", "Exports can only be generated if export symbol/property " + "functions are set."); /** Generates exports for @export annotations. */ final PassFactory generateExports = new PassFactory("generateExports", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { CodingConvention convention = compiler.getCodingConvention(); if (convention.getExportSymbolFunction() != null && convention.getExportPropertyFunction() != null) { return new GenerateExports(compiler, convention.getExportSymbolFunction(), convention.getExportPropertyFunction()); } else { return new ErrorPass(compiler, GENERATE_EXPORTS_ERROR); } } }; /** Generates exports for functions associated with JSUnit. */ final PassFactory exportTestFunctions = new PassFactory("exportTestFunctions", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { CodingConvention convention = compiler.getCodingConvention(); if (convention.getExportSymbolFunction() != null) { return new ExportTestFunctions(compiler, convention.getExportSymbolFunction(), convention.getExportPropertyFunction()); } else { return new ErrorPass(compiler, GENERATE_EXPORTS_ERROR); } } }; /** Raw exports processing pass. */ final PassFactory gatherRawExports = new PassFactory("gatherRawExports", false) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { final GatherRawExports pass = new GatherRawExports( compiler); return new CompilerPass() { @Override public void process(Node externs, Node root) { pass.process(externs, root); if (exportedNames == null) { exportedNames = Sets.newHashSet(); }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> exportedNames.addAll(pass.getExportedVariableNames()); } }; } }; /** Closure pre-processing pass. */ @SuppressWarnings("deprecation") final HotSwapPassFactory closurePrimitives = new HotSwapPassFactory("processProvidesAndRequires", false) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { maybeInitializePreprocessorSymbolTable(compiler); final ProcessClosurePrimitives pass = new ProcessClosurePrimitives( compiler, preprocessorSymbolTable, options.brokenClosureRequiresLevel, options.rewriteNewDateGoogNow); return new HotSwapCompilerPass() { @Override public void process(Node externs, Node root) { pass.process(externs, root); exportedNames = pass.getExportedVariableNames(); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { pass.hotSwapScript(scriptRoot, originalRoot); } }; } }; /** * The default i18n pass. * A lot of the options are not configurable, because ReplaceMessages * has a lot of legacy logic. */ final PassFactory replaceMessages = new PassFactory("replaceMessages", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return new ReplaceMessages(compiler, options.messageBundle, /* warn about message dupes */ true, /* allow messages with goog.getMsg */ JsMessage.Style.getFromParams(true, false), /* if we can't find a translation, don't worry about it. */ false); } }; /** Applies aliases and inlines goog.scope. */ final HotSwapPassFactory closureGoogScopeAliases = new HotSwapPassFactory("processGoogScopeAliases", true) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { maybeInitializePreprocessorSymbolTable(compiler); return new ScopedAliases( compiler, preprocessorSymbolTable, options.getAliasTransformationHandler()); } }; /** Checks that CSS class names are wrapped in goog.getCssName */ final PassFactory closureCheckGetCssName = new PassFactory("checkMissingGetCssName", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { String

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>Factory("latePeepholeOptimizations", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { final boolean late = true; return new PeepholeOptimizationsPass(compiler, new StatementFusion(), new PeepholeRemoveDeadCode(), new PeepholeSubstituteAlternateSyntax(late), new PeepholeReplaceKnownMethods(), new PeepholeFoldConstants(late), new ReorderConstantExpression()); } }; /** Checks that all variables are defined. */ final HotSwapPassFactory checkVars = new HotSwapPassFactory("checkVars", true) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { return new VarCheck(compiler); } }; /** Checks for RegExp references. */ final PassFactory checkRegExp = new PassFactory("checkRegExp", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { final CheckRegExp pass = new CheckRegExp(compiler); return new CompilerPass() { @Override public void process(Node externs, Node root) { pass.process(externs, root); compiler.setHasRegExpGlobalReferences( pass.isGlobalRegExpPropertiesUsed()); } }; } }; /** Checks that references to variables look reasonable. */ final HotSwapPassFactory checkVariableReferences = new HotSwapPassFactory("checkVariableReferences", true) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { return new VariableReferenceCheck( compiler, options.aggressiveVarCheck); } }; /** Pre-process goog.testing.ObjectPropertyString. */ final PassFactory objectPropertyStringPreprocess = new PassFactory("ObjectPropertyStringPreprocess", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new ObjectPropertyStringPreprocess(compiler); } }; /** Creates a typed scope and adds types to the type registry. */ final HotSwapPassFactory resolveTypes = new HotSwapPassFactory("resolveTypes", false) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { return new GlobalTypeResolver(compiler); } }; /** Runs type inference. */ final HotSwapPassFactory inferTypes = new HotSwap

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>PassFactory("inferTypes", false) { @Override protected HotSwapCompilerPass createInternal(final AbstractCompiler compiler) { return new HotSwapCompilerPass() { @Override public void process(Node externs, Node root) { Preconditions.checkNotNull(topScope); Preconditions.checkNotNull(getTypedScopeCreator()); makeTypeInference(compiler).process(externs, root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { makeTypeInference(compiler).inferTypes(scriptRoot); } }; } }; final HotSwapPassFactory inferJsDocInfo = new HotSwapPassFactory("inferJsDocInfo", false) { @Override protected HotSwapCompilerPass createInternal( final AbstractCompiler compiler) { return new HotSwapCompilerPass() { @Override public void process(Node externs, Node root) { Preconditions.checkNotNull(topScope); Preconditions.checkNotNull(getTypedScopeCreator()); makeInferJsDocInfo(compiler).process(externs, root); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { makeInferJsDocInfo(compiler).hotSwapScript(scriptRoot, originalRoot); } }; } }; /** Checks type usage */ final HotSwapPassFactory checkTypes = new HotSwapPassFactory("checkTypes", false) { @Override protected HotSwapCompilerPass createInternal(final AbstractCompiler compiler) { return new HotSwapCompilerPass() { @Override public void process(Node externs, Node root) { Preconditions.checkNotNull(topScope); Preconditions.checkNotNull(getTypedScopeCreator()); TypeCheck check = makeTypeCheck(compiler); check.process(externs, root); compiler.getErrorManager().setTypedPercent(check.getTypedPercent()); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { makeTypeCheck(compiler).check(scriptRoot, false); } }; } }; /** * Checks possible execution paths of the program for problems: missing return * statements and dead code. */ final HotSwapPassFactory checkControlFlow = new HotSwapPassFactory("checkControlFlow", true) { @Override protected HotSwapCompilerPass createInternal

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>(AbstractCompiler compiler) { List<Callback> callbacks = Lists.newArrayList(); if (options.checkUnreachableCode.isOn()) { callbacks.add( new CheckUnreachableCode(compiler, options.checkUnreachableCode)); } if (options.checkMissingReturn.isOn() && options.checkTypes) { callbacks.add( new CheckMissingReturn(compiler, options.checkMissingReturn)); } return combineChecks(compiler, callbacks); } }; /** Checks access controls. Depends on type-inference. */ final HotSwapPassFactory checkAccessControls = new HotSwapPassFactory("checkAccessControls", true) { @Override protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) { return new CheckAccessControls(compiler); } }; /** Executes the given callbacks with a {@link CombinedCompilerPass}. */ private static HotSwapCompilerPass combineChecks(AbstractCompiler compiler, List<Callback> callbacks) { Preconditions.checkArgument(callbacks.size() > 0); Callback[] array = callbacks.toArray(new Callback[callbacks.size()]); return new CombinedCompilerPass(compiler, array); } /** A compiler pass that resolves types in the global scope. */ class GlobalTypeResolver implements HotSwapCompilerPass { private final AbstractCompiler compiler; GlobalTypeResolver(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { if (topScope == null) { regenerateGlobalTypedScope(compiler, root.getParent()); } else { compiler.getTypeRegistry().resolveTypesInScope(topScope); } } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { patchGlobalTypedScope(compiler, scriptRoot); } } /** Checks global name usage. */ final PassFactory checkGlobalNames = new PassFactory("checkGlobalNames", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { // Create a global namespace for analysis by check passes. // Note that this class does all heavy computation lazily, // so it's OK to create it here.

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { process(null, null); } }; } }; /** Checks that all constants are not modified */ final PassFactory checkConsts = new PassFactory("checkConsts", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new ConstCheck(compiler); } }; /** Computes the names of functions for later analysis. */ final PassFactory computeFunctionNames = new PassFactory("computeFunctionNames", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return ((functionNames = new FunctionNames(compiler))); } }; /** Skips Caja-private properties in for-in loops */ final PassFactory ignoreCajaProperties = new PassFactory("ignoreCajaProperties", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new IgnoreCajaProperties(compiler); } }; /** Inserts runtime type assertions for debugging. */ final PassFactory runtimeTypeCheck = new PassFactory("runtimeTypeCheck", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new RuntimeTypeCheck(compiler, options.runtimeTypeCheckLogFunction); } }; /** Generates unique ids. */ final PassFactory replaceIdGenerators = new PassFactory("replaceIdGenerators", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { ReplaceIdGenerators pass = new ReplaceIdGenerators(compiler, options.idGenerators); pass.process(externs, root); idGeneratorMap = pass.getIdGeneratorMap(); } }; } }; /** Replace strings. */ final PassFactory replaceStrings = new PassFactory("replaceStrings", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { ReplaceStrings pass = new ReplaceStrings( compiler, options.replaceStringsPlaceholderToken, options.replaceStringsFunctionDescriptions, options.replaceStringsReservedStrings); pass.process(externs, root); stringMap

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> }; /** * Use data flow analysis to remove dead branches. */ final PassFactory removeUnreachableCode = new PassFactory("removeUnreachableCode", false) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new UnreachableCodeElimination(compiler, true); } }; /** * Remove prototype properties that do not appear to be used. */ final PassFactory removeUnusedPrototypeProperties = new PassFactory("removeUnusedPrototypeProperties", false) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new RemoveUnusedPrototypeProperties( compiler, options.removeUnusedPrototypePropertiesInExterns, !options.removeUnusedVars); } }; /** * Remove prototype properties that do not appear to be used. */ final PassFactory removeUnusedClassProperties = new PassFactory("removeUnusedClassProperties", false) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new RemoveUnusedClassProperties(compiler); } }; /** * Process smart name processing - removes unused classes and does referencing * starting with minimum set of names. */ final PassFactory smartNamePass = new PassFactory("smartNamePass", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { NameAnalyzer na = new NameAnalyzer(compiler, false); na.process(externs, root); String reportPath = options.reportPath; if (reportPath != null) { try { Files.write(na.getHtmlReport(), new File(reportPath), Charsets.UTF_8); } catch (IOException e) { compiler.report(JSError.make(REPORT_PATH_IO_ERROR, reportPath)); } } if (options.smartNameRemoval) { na.removeUnreferenced(); } } }; } }; /** * Process smart name processing - removes unused classes and does referencing * starting with minimum set of names. */ final PassFactory smartNamePass2 = new PassFactory("smartNamePass", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return new CompilerPass

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>() { @Override public void process(Node externs, Node root) { NameAnalyzer na = new NameAnalyzer(compiler, false); na.process(externs, root); na.removeUnreferenced(); } }; } }; /** Inlines simple methods, like getters */ final PassFactory inlineSimpleMethods = new PassFactory("inlineSimpleMethods", false) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new InlineSimpleMethods(compiler); } }; /** Kills dead assignments. */ final PassFactory deadAssignmentsElimination = new PassFactory("deadAssignmentsElimination", false) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new DeadAssignmentsElimination(compiler); } }; /** Inlines function calls. */ final PassFactory inlineFunctions = new PassFactory("inlineFunctions", false) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { boolean enableBlockInlining = !isInliningForbidden(); return new InlineFunctions( compiler, compiler.getUniqueNameIdSupplier(), options.inlineFunctions, options.inlineLocalFunctions, enableBlockInlining, options.assumeStrictThis() || options.getLanguageIn() == LanguageMode.ECMASCRIPT5_STRICT, options.assumeClosuresOnlyCaptureReferences); } }; /** Removes variables that are never used. */ final PassFactory removeUnusedVars = new PassFactory("removeUnusedVars", false) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { boolean removeOnlyLocals = options.removeUnusedLocalVars && !options.removeUnusedVars; boolean preserveAnonymousFunctionNames = options.anonymousFunctionNaming != AnonymousFunctionNamingPolicy.OFF; return new RemoveUnusedVars( compiler, !removeOnlyLocals, preserveAnonymousFunctionNames, false); } }; /** * Move global symbols to a deeper common module */ final PassFactory crossModuleCodeMotion = new PassFactory("crossModuleCodeMotion", false) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new CrossModuleCodeMotion(compiler, compiler.getModuleGraph()); } }; /** * Move methods to a deeper common module */ final PassFactory

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>groupVariableDeclarations", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new GroupVariableDeclarations(compiler); } }; /** * Extracts common sub-expressions. */ final PassFactory extractPrototypeMemberDeclarations = new PassFactory("extractPrototypeMemberDeclarations", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new ExtractPrototypeMemberDeclarations( compiler, Pattern.USE_GLOBAL_TEMP); } }; /** Rewrites common function definitions to be more compact. */ final PassFactory rewriteFunctionExpressions = new PassFactory("rewriteFunctionExpressions", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new FunctionRewriter(compiler); } }; /** Collapses functions to not use the VAR keyword. */ final PassFactory collapseAnonymousFunctions = new PassFactory("collapseAnonymousFunctions", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new CollapseAnonymousFunctions(compiler); } }; /** Moves function declarations to the top, to simulate actual hoisting. */ final PassFactory moveFunctionDeclarations = new PassFactory("moveFunctionDeclarations", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new MoveFunctionDeclarations(compiler); } }; final PassFactory nameUnmappedAnonymousFunctions = new PassFactory("nameAnonymousFunctions", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new NameAnonymousFunctions(compiler); } }; final PassFactory nameMappedAnonymousFunctions = new PassFactory("nameAnonymousFunctions", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { NameAnonymousFunctionsMapped naf = new NameAnonymousFunctionsMapped(compiler); naf.process(externs, root); anonymousFunctionNameMap = naf.getFunctionMap(); } }; } }; /** Alias external symbols. */ final PassFactory aliasExternals = new PassFactory("aliasExternals", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new AliasExternals(compiler, compiler.getModuleGraph

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>(), options.unaliasableGlobals, options.aliasableGlobals); } }; /** * Alias string literals with global variables, to avoid creating lots of * transient objects. */ final PassFactory aliasStrings = new PassFactory("aliasStrings", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new AliasStrings( compiler, compiler.getModuleGraph(), options.aliasAllStrings ? null : options.aliasableStrings, options.aliasStringsBlacklist, options.outputJsStringUsage); } }; /** Aliases common keywords (true, false) */ final PassFactory aliasKeywords = new PassFactory("aliasKeywords", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new AliasKeywords(compiler); } }; /** Handling for the ObjectPropertyString primitive. */ final PassFactory objectPropertyStringPostprocess = new PassFactory("ObjectPropertyStringPostprocess", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new ObjectPropertyStringPostprocess(compiler); } }; /** * Renames properties so that the two properties that never appear on * the same object get the same name. */ final PassFactory ambiguateProperties = new PassFactory("ambiguateProperties", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new AmbiguateProperties( compiler, options.anonymousFunctionNaming.getReservedCharacters()); } }; /** * Mark the point at which the normalized AST assumptions no longer hold. */ final PassFactory markUnnormalized = new PassFactory("markUnnormalized", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { compiler.setLifeCycleStage(LifeCycleStage.RAW); } }; } }; /** Denormalize the AST for code generation. */ final PassFactory denormalize = new PassFactory("denormalize", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new Denormalize(compiler); } }; /** Inverting name normalization. */ final PassFactory invertContext

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>ualRenaming = new PassFactory("invertNames", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return MakeDeclaredNamesUnique.getContextualRenameInverter(compiler); } }; /** * Renames properties. */ final PassFactory renameProperties = new PassFactory("renameProperties", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { VariableMap map = null; if (options.inputPropertyMapSerialized != null) { try { map = VariableMap.fromBytes(options.inputPropertyMapSerialized); } catch (ParseException e) { return new ErrorPass(compiler, JSError.make(INPUT_MAP_PROP_PARSE, e.getMessage())); } } final VariableMap prevPropertyMap = map; return new CompilerPass() { @Override public void process(Node externs, Node root) { propertyMap = runPropertyRenaming( compiler, prevPropertyMap, externs, root); } }; } }; private VariableMap runPropertyRenaming( AbstractCompiler compiler, VariableMap prevPropertyMap, Node externs, Node root) { char[] reservedChars = options.anonymousFunctionNaming.getReservedCharacters(); switch (options.propertyRenaming) { case HEURISTIC: RenamePrototypes rproto = new RenamePrototypes(compiler, false, reservedChars, prevPropertyMap); rproto.process(externs, root); return rproto.getPropertyMap(); case AGGRESSIVE_HEURISTIC: RenamePrototypes rproto2 = new RenamePrototypes(compiler, true, reservedChars, prevPropertyMap); rproto2.process(externs, root); return rproto2.getPropertyMap(); case ALL_UNQUOTED: RenameProperties rprop = new RenameProperties( compiler, options.propertyAffinity, options.generatePseudoNames, prevPropertyMap, reservedChars); rprop.process(externs, root); return rprop.getPropertyMap(); default: throw new IllegalStateException( "Unrecognized property renaming policy"); } } /** Renames variables. */ final PassFactory renameVars = new PassFactory("renameVars", true) { @Override protected CompilerPass createInternal(final AbstractCompiler

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> compiler) { VariableMap map = null; if (options.inputVariableMapSerialized != null) { try { map = VariableMap.fromBytes(options.inputVariableMapSerialized); } catch (ParseException e) { return new ErrorPass(compiler, JSError.make(INPUT_MAP_VAR_PARSE, e.getMessage())); } } final VariableMap prevVariableMap = map; return new CompilerPass() { @Override public void process(Node externs, Node root) { variableMap = runVariableRenaming( compiler, prevVariableMap, externs, root); } }; } }; private VariableMap runVariableRenaming( AbstractCompiler compiler, VariableMap prevVariableMap, Node externs, Node root) { char[] reservedChars = options.anonymousFunctionNaming.getReservedCharacters(); boolean preserveAnonymousFunctionNames = options.anonymousFunctionNaming != AnonymousFunctionNamingPolicy.OFF; RenameVars rn = new RenameVars( compiler, options.renamePrefix, options.variableRenaming == VariableRenamingPolicy.LOCAL, preserveAnonymousFunctionNames, options.generatePseudoNames, options.shadowVariables, prevVariableMap, reservedChars, exportedNames); rn.process(externs, root); return rn.getVariableMap(); } /** Renames labels */ final PassFactory renameLabels = new PassFactory("renameLabels", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new RenameLabels(compiler); } }; /** Convert bracket access to dot access */ final PassFactory convertToDottedProperties = new PassFactory("convertToDottedProperties", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new ConvertToDottedProperties(compiler); } }; /** Checks that all variables are defined. */ final PassFactory sanityCheckAst = new PassFactory("sanityCheckAst", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return new AstValidator(); } }; /** Checks that all variables are defined. */ final PassFactory sanityCheckVars = new PassFactory("sanityCheckVars", true) { @Override protected CompilerPass createInternal(AbstractCompiler compiler) { return

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> new VarCheck(compiler, true); } }; /** Adds instrumentations according to an instrumentation template. */ final PassFactory instrumentFunctions = new PassFactory("instrumentFunctions", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node root) { try { FileReader templateFile = new FileReader(options.instrumentationTemplate); (new InstrumentFunctions( compiler, functionNames, options.instrumentationTemplate, options.appNameStr, templateFile)).process(externs, root); } catch (IOException e) { compiler.report( JSError.make(AbstractCompiler.READ_ERROR, options.instrumentationTemplate)); } } }; } }; /** * Create a no-op pass that can only run once. Used to break up loops. */ static PassFactory createEmptyPass(String name) { return new PassFactory(name, true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return runInSerial(); } }; } /** * Runs custom passes that are designated to run at a particular time. */ private PassFactory getCustomPasses( final CustomPassExecutionTime executionTime) { return new PassFactory("runCustomPasses", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return runInSerial(options.customPasses.get(executionTime)); } }; } /** * All inlining is forbidden in heuristic renaming mode, because inlining * will ruin the invariants that it depends on. */ private boolean isInliningForbidden() { return options.propertyRenaming == PropertyRenamingPolicy.HEURISTIC || options.propertyRenaming == PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC; } /** Create a compiler pass that runs the given passes in serial. */ private static CompilerPass runInSerial(final CompilerPass ... passes) { return runInSerial(Lists.newArrayList(passes)); } /** Create a compiler pass that runs the given passes in serial. */ private static CompilerPass runInSerial( final Collection<CompilerPass> passes) { return new CompilerPass() {

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> @Override public void process(Node externs, Node root) { for (CompilerPass pass : passes) { pass.process(externs, root); } } }; } @VisibleForTesting static Map<String, Node> getAdditionalReplacements( CompilerOptions options) { Map<String, Node> additionalReplacements = Maps.newHashMap(); if (options.markAsCompiled || options.closurePass) { additionalReplacements.put(COMPILED_CONSTANT_NAME, IR.trueNode()); } if (options.closurePass && options.locale != null) { additionalReplacements.put(CLOSURE_LOCALE_CONSTANT_NAME, IR.string(options.locale)); } return additionalReplacements; } final PassFactory printNameReferenceGraph = new PassFactory("printNameReferenceGraph", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { NameReferenceGraphConstruction gc = new NameReferenceGraphConstruction(compiler); gc.process(externs, jsRoot); String graphFileName = options.nameReferenceGraphPath; try { Files.write(DotFormatter.toDot(gc.getNameReferenceGraph()), new File(graphFileName), Charsets.UTF_8); } catch (IOException e) { compiler.report( JSError.make( NAME_REF_GRAPH_FILE_ERROR, e.getMessage(), graphFileName)); } } }; } }; final PassFactory printNameReferenceReport = new PassFactory("printNameReferenceReport", true) { @Override protected CompilerPass createInternal(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { NameReferenceGraphConstruction gc = new NameReferenceGraphConstruction(compiler); String reportFileName = options.nameReferenceReportPath; try { NameReferenceGraphReport report = new NameReferenceGraphReport(gc.getNameReferenceGraph()); Files.write(report.getHtmlReport(), new File(reportFileName), Charsets.UTF_8); } catch (IOException e) { compiler.report( JSError.

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bob Jervis * Google Inc. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino.jstype; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; /** * A builder for the Rhino Node representing Function parameters. * @author nicksantos@google.com (Nick Santos) */ public class FunctionParamBuilder { private final JSTypeRegistry registry; private final Node root = new Node(Token.PARAM_LIST);

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>String(Token.NAME, ""); paramNode.setJSType(type); root.addChildToBack(paramNode); return paramNode; } public Node build() { return root; } private boolean hasOptionalOrVarArgs() { Node lastChild = root.getLastChild(); return lastChild != null && (lastChild.isOptionalArg() || lastChild.isVarArgs()); } public boolean hasVarArgs() { Node lastChild = root.getLastChild(); return lastChild != null && lastChild.isVarArgs(); } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> */ public class ScriptRuntime { /** * No instances should be created. */ protected ScriptRuntime() { } // It is public so NativeRegExp can access it . public static boolean isJSLineTerminator(int c) { // Optimization for faster check for eol character: // they do not have 0xDFD0 bits set if ((c & 0xDFD0) != 0) { return false; } return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029; } // Can not use Double.NaN defined as 0.0d / 0.0 as under the Microsoft VM, // versions 2.01 and 3.0P1, that causes some uses (returns at least) of // Double.NaN to be converted to 1.0. // So we use ScriptRuntime.NaN instead of Double.NaN. public static final double NaN = Double.longBitsToDouble(0x7ff8000000000000L); // A similar problem exists for negative zero. public static final double negativeZero = Double.longBitsToDouble(0x8000000000000000L); /* * Helper function for toNumber, parseInt, and TokenStream.getToken. */ @SuppressWarnings("fallthrough") static double stringToNumber(String s, int start, int radix) { char digitMax = '9'; char lowerCaseBound = 'a'; char upperCaseBound = 'A'; int len = s.length(); if (radix < 10) { digitMax = (char) ('0' + radix - 1); } if (radix > 10) { lowerCaseBound = (char) ('a' + radix - 10); upperCaseBound = (char) ('A' + radix - 10); } int end; double sum = 0.0; for (end=start; end < len; end++) { char c = s.charAt(end); int newDigit; if ('0' <= c && c <= digitMax) newDigit

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> = c - '0'; else if ('a' <= c && c < lowerCaseBound) newDigit = c - 'a' + 10; else if ('A' <= c && c < upperCaseBound) newDigit = c - 'A' + 10; else break; sum = sum*radix + newDigit; } if (start == end) { return NaN; } if (sum >= 9007199254740992.0) { if (radix == 10) { /* If we're accumulating a decimal number and the number * is >= 2^53, then the result from the repeated multiply-add * above may be inaccurate. Call Java to get the correct * answer. */ try { return Double.valueOf(s.substring(start, end)).doubleValue(); } catch (NumberFormatException nfe) { return NaN; } } else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) { /* The number may also be inaccurate for one of these bases. * This happens if the addition in value*radix + digit causes * a round-down to an even least significant mantissa bit * when the first dropped bit is a one. If any of the * following digits in the number (which haven't been added * in yet) are nonzero then the correct action would have * been to round up instead of down. An example of this * occurs when reading the number 0x1000000000000081, which * rounds to 0x1000000000000000 instead of 0x1000000000000100. */ int bitShiftInChar = 1; int digit = 0; final int SKIP_LEADING_ZEROS = 0; final int FIRST_EXACT_53_BITS = 1; final int AFTER_BIT_53 = 2; final int ZEROS_AFTER_54 = 3;

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> final int MIXED_AFTER_54 = 4; int state = SKIP_LEADING_ZEROS; int exactBitsLimit = 53; double factor = 0.0; boolean bit53 = false; // bit54 is the 54th bit (the first dropped from the mantissa) boolean bit54 = false; for (;;) { if (bitShiftInChar == 1) { if (start == end) break; digit = s.charAt(start++); if ('0' <= digit && digit <= '9') digit -= '0'; else if ('a' <= digit && digit <= 'z') digit -= 'a' - 10; else digit -= 'A' - 10; bitShiftInChar = radix; } bitShiftInChar >>= 1; boolean bit = (digit & bitShiftInChar) != 0; switch (state) { case SKIP_LEADING_ZEROS: if (bit) { --exactBitsLimit; sum = 1.0; state = FIRST_EXACT_53_BITS; } break; case FIRST_EXACT_53_BITS: sum *= 2.0; if (bit) sum += 1.0; --exactBitsLimit; if (exactBitsLimit == 0) { bit53 = bit; state = AFTER_BIT_53; } break; case AFTER_BIT_53: bit54 = bit; factor = 2.0; state = ZEROS_AFTER_54; break; case ZEROS_AFTER_54: if (bit) { state = MIXED_AFTER_54; } // fallthrough case MIXED_AFTER_54: factor *= 2; break; } } switch (state) { case SKIP_LEADING_ZEROS: sum = 0.0; break; case FIRST_EXACT_53_BITS: case AFTER_BIT_53: // do nothing break; case ZEROS_AFTER_54: // x1.1 -> x

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>1 + 1 (round up) // x0.1 -> x0 (round down) if (bit54 & bit53) sum += 1.0; sum *= factor; break; case MIXED_AFTER_54: // x.100...1.. -> x + 1 (round up) // x.0anything -> x (round down) if (bit54) sum += 1.0; sum *= factor; break; } } /* We don't worry about inaccurate numbers for any other base. */ } return sum; } public static String escapeString(String s) { return escapeString(s, '"'); } /** * For escaping strings printed by object and array literals; not quite * the same as 'escape.' */ public static String escapeString(String s, char escapeQuote) { if (!(escapeQuote == '"' || escapeQuote == '\'')) { throw new IllegalStateException("unexpected quote char:" + escapeQuote); } StringBuffer sb = null; for(int i = 0, L = s.length(); i != L; ++i) { int c = s.charAt(i); if (' ' <= c && c <= '~' && c != escapeQuote && c != '\\') { // an ordinary print character (like C isprint()) and not " // or \ . if (sb != null) { sb.append((char)c); } continue; } if (sb == null) { sb = new StringBuffer(L + 3); sb.append(s); sb.setLength(i); } int escape = -1; switch (c) { case '\b': escape = 'b'; break; case '\f': escape = 'f'; break; case '\n': escape = 'n'; break; case '\r': escape = 'r'; break; case '\t': escape = 't'; break; case 0xb: escape = 'v'; break; // Java lacks \v. case ' ': escape = ' '; break; case '\\': escape = '\\'; break; } if (

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>escape >= 0) { // an \escaped sort of character sb.append('\\'); sb.append((char)escape); } else if (c == escapeQuote) { sb.append('\\'); sb.append(escapeQuote); } else { int hexSize; if (c < 256) { // 2-digit hex sb.append("\\x"); hexSize = 2; } else { // Unicode. sb.append("\\u"); hexSize = 4; } // append hexadecimal form of c left-padded with 0 for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) { int digit = 0xf & (c >> shift); int hc = (digit < 10) ? '0' + digit : 'a' - 10 + digit; sb.append((char)hc); } } } return (sb == null) ? s : sb.toString(); } static boolean isValidIdentifierName(String s) { int L = s.length(); if (L == 0) return false; if (!Character.isJavaIdentifierStart(s.charAt(0))) return false; for (int i = 1; i != L; ++i) { if (!Character.isJavaIdentifierPart(s.charAt(i))) return false; } return !TokenStream.isKeyword(s); } /** * If str is a decimal presentation of Uint32 value, return it as long. * Othewise return -1L; */ public static long testUint32String(String str) { // The length of the decimal string representation of // UINT32_MAX_VALUE, 4294967296 final int MAX_VALUE_LENGTH = 10; int len = str.length(); if (1 <= len && len <= MAX_VALUE_LENGTH) { int c = str.charAt(0); c -= '0'; if (c == 0) { // Note that 00,01 etc. are not valid Uint32 presentations return (len == 1) ? 0L :

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> -1L; } if (1 <= c && c <= 9) { long v = c; for (int i = 1; i != len; ++i) { c = str.charAt(i) - '0'; if (!(0 <= c && c <= 9)) { return -1; } v = 10 * v + c; } // Check for overflow if ((v >>> 32) == 0) { return v; } } } return -1; } static boolean isSpecialProperty(String s) { return s.equals("__proto__") || s.equals("__parent__"); } // ------------------ // Statements // ------------------ public static String getMessage0(String messageId) { return getMessage(messageId, null); } public static String getMessage1(String messageId, Object arg1) { Object[] arguments = {arg1}; return getMessage(messageId, arguments); } /* OPT there's a noticable delay for the first error! Maybe it'd * make sense to use a ListResourceBundle instead of a properties * file to avoid (synchronized) text parsing. */ public static String getMessage(String messageId, Object[] arguments) { final String defaultResource = "rhino_ast.java.com.google.javascript.rhino.Messages"; Locale locale = Locale.getDefault(); // ResourceBundle does cacheing. ResourceBundle rb = ResourceBundle.getBundle(defaultResource, locale); String formatString; try { formatString = rb.getString(messageId); } catch (java.util.MissingResourceException mre) { throw new RuntimeException ("no message resource found for message property "+ messageId); } /* * It's OK to format the string, even if 'arguments' is null; * we need to format it anyway, to make double ''s collapse to * single 's. */ // TODO: MessageFormat is not available on pJava MessageFormat formatter = new MessageFormat(formatString); return formatter.format(arguments); } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>/* * Copyright 2006 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.Node; /** * <p>Interface for classes that can compile JS.</p> * * <p>Class has single function "process", which is passed * the root node of the parsed JS tree, as well as the * root node of the external JS tree (used to provide a public API * and prevent renaming of system functions).</p> * * <p>Use this class to support testing with BaseCompilerTest</p> * */ public interface CompilerPass { /** * Process the JS with root node root. * Can modify the contents of each Node tree * @param externs Top of external JS tree * @param root Top of JS tree */ void process(Node externs, Node root); }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> fileName, InputStream s) throws IOException { return new JSSourceFile(SourceFile.fromInputStream(fileName, s)); } public static JSSourceFile fromGenerator(String fileName, Generator generator) { return new JSSourceFile(SourceFile.fromGenerator(fileName, generator)); } private SourceFile referenced; private JSSourceFile(SourceFile referenced) { super(referenced.getName()); this.referenced = referenced; } @Override public String getCode() throws IOException { return referenced.getCode(); } @Override public void clearCachedSource() { referenced.clearCachedSource(); } @Override @VisibleForTesting String getCodeNoCache() { return referenced.getCodeNoCache(); } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> public CompilerInput(SourceAst ast) { this(ast, ast.getSourceFile().getName(), false); } public CompilerInput(SourceAst ast, boolean isExtern) { this(ast, ast.getInputId(), isExtern); } public CompilerInput(SourceAst ast, String inputId, boolean isExtern) { this(ast, new InputId(inputId), isExtern); } public CompilerInput(SourceAst ast, InputId inputId, boolean isExtern) { this.ast = ast; this.id = inputId; // TODO(nicksantos): Add a precondition check here. People are passing // in null, but they should not be. if (ast != null && ast.getSourceFile() != null) { ast.getSourceFile().setIsExtern(isExtern); } } public CompilerInput(JSSourceFile file) { this(file, false); } public CompilerInput(JSSourceFile file, boolean isExtern) { this(new JsAst(file), isExtern); } /** Returns a name for this input. Must be unique across all inputs. */ @Override public InputId getInputId() { return id; } /** Returns a name for this input. Must be unique across all inputs. */ @Override public String getName() { return id.getIdName(); } public SourceAst getAst() { return ast; } /** Gets the path relative to closure-base, if one is available. */ @Override public String getPathRelativeToClosureBase() { // TODO(nicksantos): Implement me. throw new UnsupportedOperationException(); } @Override public Node getAstRoot(AbstractCompiler compiler) { Node root = ast.getAstRoot(compiler); // The root maybe null if the AST can not be created. if (root != null) { Preconditions.checkState(root.isScript()); Preconditions.checkNotNull(root.getInputId()); } return root; } @Override public void clearAst() { ast.clearAst(); } @Override public SourceFile getSourceFile() { return ast.getSourceFile(); } @Override public void setSourceFile(SourceFile file) { ast

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>.setSourceFile(file); } /** Returns the SourceAst object on which this input is based. */ public SourceAst getSourceAst() { return ast; } /** Sets an error manager for routing error messages. */ public void setErrorManager(ErrorManager errorManager) { this.errorManager = errorManager; } /** Sets an abstract compiler for doing parsing. */ public void setCompiler(AbstractCompiler compiler) { this.compiler = compiler; setErrorManager(compiler.getErrorManager()); } /** Gets a list of types depended on by this input. */ @Override public Collection<String> getRequires() { Preconditions.checkNotNull(errorManager, "Expected setErrorManager to be called first"); try { regenerateDependencyInfoIfNecessary(); return Collections.<String>unmodifiableSet(requires); } catch (IOException e) { errorManager.report(CheckLevel.ERROR, JSError.make(AbstractCompiler.READ_ERROR, getName())); return ImmutableList.<String>of(); } } /** Gets a list of types provided by this input. */ @Override public Collection<String> getProvides() { Preconditions.checkNotNull(errorManager, "Expected setErrorManager to be called first"); try { regenerateDependencyInfoIfNecessary(); return Collections.<String>unmodifiableSet(provides); } catch (IOException e) { errorManager.report(CheckLevel.ERROR, JSError.make(AbstractCompiler.READ_ERROR, getName())); return ImmutableList.<String>of(); } } /** * Regenerates the provides/requires if we need to do so. */ private void regenerateDependencyInfoIfNecessary() throws IOException { // If the code is NOT a JsAst, then it was not originally JS code. // Look at the Ast for dependency info. if (!(ast instanceof JsAst)) { Preconditions.checkNotNull(compiler, "Expected setCompiler to be called first"); DepsFinder finder = new DepsFinder(); Node root = getAstRoot(compiler); if (root == null) { return; } finder.visitTree(getAstRoot(compiler)); // TODO(nicksantos|user): This caching behavior is a bit // odd, and only works if you assume the exact call flow that

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> method effectively emulates the * <code>Number()</code> JavaScript cast function. */ static Double getNumberValue(Node n) { switch (n.getType()) { case Token.TRUE: return 1.0; case Token.FALSE: case Token.NULL: return 0.0; case Token.NUMBER: return n.getDouble(); case Token.VOID: if (mayHaveSideEffects(n.getFirstChild())) { return null; } else { return Double.NaN; } case Token.NAME: // Check for known constants String name = n.getString(); if (name.equals("undefined")) { return Double.NaN; } if (name.equals("NaN")) { return Double.NaN; } if (name.equals("Infinity")) { return Double.POSITIVE_INFINITY; } return null; case Token.NEG: if (n.getChildCount() == 1 && n.getFirstChild().isName() && n.getFirstChild().getString().equals("Infinity")) { return Double.NEGATIVE_INFINITY; } return null; case Token.NOT: TernaryValue child = getPureBooleanValue(n.getFirstChild()); if (child != TernaryValue.UNKNOWN) { return child.toBoolean(true) ? 0.0 : 1.0; // reversed. } break; case Token.STRING: return getStringNumberValue(n.getString()); case Token.ARRAYLIT: case Token.OBJECTLIT: String value = getStringValue(n); return value != null ? getStringNumberValue(value) : null; } return null; } static Double getStringNumberValue(String rawJsString) { if (rawJsString.contains("\u000b")) { // vertical tab is not always whitespace return null; } String s = trimJsWhiteSpace(rawJsString); // return ScriptRuntime.toNumber(s); if (s.length() == 0) { return 0.0; } if (s.length() > 2 && s.charAt(0) == '0' && (s.charAt(1) == 'x' || s.charAt(1) == 'X

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>')) { // Attempt to convert hex numbers. try { return Double.valueOf(Integer.parseInt(s.substring(2), 16)); } catch (NumberFormatException e) { return Double.NaN; } } if (s.length() > 3 && (s.charAt(0) == '-' || s.charAt(0) == '+') && s.charAt(1) == '0' && (s.charAt(2) == 'x' || s.charAt(2) == 'X')) { // hex numbers with explicit signs vary between browsers. return null; } // FireFox and IE treat the "Infinity" differently. FireFox is case // insensitive, but IE treats "infinity" as NaN. So leave it alone. if (s.equals("infinity") || s.equals("-infinity") || s.equals("+infinity")) { return null; } try { return Double.parseDouble(s); } catch (NumberFormatException e) { return Double.NaN; } } static String trimJsWhiteSpace(String s) { int start = 0; int end = s.length(); while (end > 0 && isStrWhiteSpaceChar(s.charAt(end - 1)) == TernaryValue.TRUE) { end--; } while (start < end && isStrWhiteSpaceChar(s.charAt(start)) == TernaryValue.TRUE) { start++; } return s.substring(start, end); } /** * Copied from Rhino's ScriptRuntime */ static TernaryValue isStrWhiteSpaceChar(int c) { switch (c) { case '\u000B': // <VT> return TernaryValue.UNKNOWN; // IE says "no", EcmaScript says "yes" case ' ': // <SP> case '\n': // <LF> case '\r': // <CR> case '\t': // <TAB> case '\u00A0': // <NBSP> case '\u000C': // <FF> case '\u2028': // <LS> case '\u2029': // <PS> case '\uFEFF

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>Value(child, includeFunctions)) { return false; } } return true; case Token.OBJECTLIT: // Return true only if all values are const. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child.getFirstChild(), includeFunctions)) { return false; } } return true; case Token.FUNCTION: return includeFunctions && !NodeUtil.isFunctionDeclaration(n); default: return isImmutableValue(n); } } /** * Determines whether the given value may be assigned to a define. * * @param val The value being assigned. * @param defines The list of names of existing defines. */ static boolean isValidDefineValue(Node val, Set<String> defines) { switch (val.getType()) { case Token.STRING: case Token.NUMBER: case Token.TRUE: case Token.FALSE: return true; // Binary operators are only valid if both children are valid. case Token.ADD: case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.DIV: case Token.EQ: case Token.GE: case Token.GT: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.URSH: return isValidDefineValue(val.getFirstChild(), defines) && isValidDefineValue(val.getLastChild(), defines); // Uniary operators are valid if the child is valid. case Token.NOT: case Token.NEG: case Token.POS: return isValidDefineValue(val.getFirstChild(), defines); // Names are valid if and only if they are defines themselves. case Token.NAME: case Token.GETPROP: if (val.isQualifiedName()) { return defines.contains(val.getQualifiedName()); } } return false; } /** * Returns whether this a BLOCK node with no children. * * @param

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> "var x = 0;". * * @param n The node * @return True if n is an L-value. */ static boolean isLValue(Node n) { Preconditions.checkArgument(n.isName() || n.isGetProp() || n.isGetElem()); Node parent = n.getParent(); return (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n) || (NodeUtil.isForIn(parent) && parent.getFirstChild() == n) || parent.isVar() || (parent.isFunction() && parent.getFirstChild() == n) || parent.isDec() || parent.isInc() || parent.isParamList() || parent.isCatch(); } /** * Determines whether a node represents an object literal key * (e.g. key1 in {key1: value1, key2: value2}). * * @param node A node * @param parent The node's parent */ static boolean isObjectLitKey(Node node, Node parent) { switch (node.getType()) { case Token.STRING: return parent.isObjectLit(); case Token.GETTER_DEF: case Token.SETTER_DEF: return true; } return false; } /** * Get the name of an object literal key. * * @param key A node */ static String getObjectLitKeyName(Node key) { switch (key.getType()) { case Token.STRING: case Token.GETTER_DEF: case Token.SETTER_DEF: return key.getString(); } throw new IllegalStateException("Unexpected node type: " + key); } /** * @param key A OBJECTLIT key node. * @return The type expected when using the key. */ static JSType getObjectLitKeyTypeFromValueType(Node key, JSType valueType) { if (valueType != null) { switch (key.getType()) { case Token.GETTER_DEF: // GET must always return a function type. if (valueType.isFunctionType()) { FunctionType fntype = valueType.toMaybeFunctionType(); valueType = fntype.getReturnType(); } else { return null;

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> */ static void redeclareVarsInsideBranch(Node branch) { Collection<Node> vars = getVarsDeclaredInBranch(branch); if (vars.isEmpty()) { return; } Node parent = getAddingRoot(branch); for (Node nameNode : vars) { Node var = IR.var( IR.name(nameNode.getString()) .srcref(nameNode)) .srcref(nameNode); copyNameAnnotations(nameNode, var.getFirstChild()); parent.addChildToFront(var); } } /** * Copy any annotations that follow a named value. * @param source * @param destination */ static void copyNameAnnotations(Node source, Node destination) { if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) { destination.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } /** * Gets a Node at the top of the current scope where we can add new var * declarations as children. */ private static Node getAddingRoot(Node n) { Node addingRoot = null; Node ancestor = n; while (null != (ancestor = ancestor.getParent())) { int type = ancestor.getType(); if (type == Token.SCRIPT) { addingRoot = ancestor; break; } else if (type == Token.FUNCTION) { addingRoot = ancestor.getLastChild(); break; } } // make sure that the adding root looks ok Preconditions.checkState(addingRoot.isBlock() || addingRoot.isScript()); Preconditions.checkState(addingRoot.getFirstChild() == null || !addingRoot.getFirstChild().isScript()); return addingRoot; } /** * Creates a node representing a qualified name. * * @param name A qualified name (e.g. "foo" or "foo.bar.baz") * @return A NAME or GETPROP node */ public static Node newQualifiedNameNode( CodingConvention convention, String name) { int endPos = name.indexOf('.'); if (endPos == -1) { return newName(convention, name); } Node node = newName(convention, name.substring(0, endPos)); int startPos; do { startPos =

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> endPos + 1; endPos = name.indexOf('.', startPos); String part = (endPos == -1 ? name.substring(startPos) : name.substring(startPos, endPos)); Node propNode = IR.string(part); if (convention.isConstantKey(part)) { propNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } node = IR.getprop(node, propNode); } while (endPos != -1); return node; } /** * Creates a node representing a qualified name, copying over the source * location information from the basis node and assigning the given original * name to the node. * * @param name A qualified name (e.g. "foo" or "foo.bar.baz") * @param basisNode The node that represents the name as currently found in * the AST. * @param originalName The original name of the item being represented by the * NAME node. Used for debugging information. * * @return A NAME or GETPROP node */ static Node newQualifiedNameNode( CodingConvention convention, String name, Node basisNode, String originalName) { Node node = newQualifiedNameNode(convention, name); setDebugInformation(node, basisNode, originalName); return node; } /** * Gets the root node of a qualified name. Must be either NAME or THIS. */ public static Node getRootOfQualifiedName(Node qName) { for (Node current = qName; true; current = current.getFirstChild()) { if (current.isName() || current.isThis()) { return current; } Preconditions.checkState(current.isGetProp()); } } /** * Sets the debug information (source file info and orignal name) * on the given node. * * @param node The node on which to set the debug information. * @param basisNode The basis node from which to copy the source file info. * @param originalName The original name of the node. */ static void setDebugInformation(Node node, Node basisNode, String originalName) { node.copyInformationFromForTree(basisNode); node.putProp(Node.

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>in set. */ static boolean isLatin(String s) { char LARGEST_BASIC_LATIN = 0x7f; int len = s.length(); for (int index = 0; index < len; index++) { char c = s.charAt(index); if (c > LARGEST_BASIC_LATIN) { return false; } } return true; } /** * Determines whether the given name is a valid variable name. */ public static boolean isValidSimpleName(String name) { return TokenStream.isJSIdentifier(name) && !TokenStream.isKeyword(name) && // no Unicode escaped characters - some browsers are less tolerant // of Unicode characters that might be valid according to the // language spec. // Note that by this point, unicode escapes have been converted // to UTF-16 characters, so we're only searching for character // values, not escapes. isLatin(name); } /** * Determines whether the given name is a valid qualified name. */ // TODO(nicksantos): This should be moved into a "Language" API, // so that the results are different for es5 and es3. public static boolean isValidQualifiedName(String name) { if (name.endsWith(".") || name.startsWith(".")) { return false; } String[] parts = name.split("\\."); for (String part : parts) { if (!isValidSimpleName(part)) { return false; } } return true; } /** * Determines whether the given name can appear on the right side of * the dot operator. Many properties (like reserved words) cannot. */ static boolean isValidPropertyName(String name) { return isValidSimpleName(name); } private static class VarCollector implements Visitor { final Map<String, Node> vars = Maps.newLinkedHashMap(); @Override public void visit(Node n) { if (n.isName()) { Node parent = n.getParent(); if (parent != null && parent.isVar()) { String name = n.getString(); if (!vars.containsKey(name)) { vars.put(name, n); } } } } } /**

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> * Retrieves vars declared in the current node tree, excluding descent scopes. */ public static Collection<Node> getVarsDeclaredInBranch(Node root) { VarCollector collector = new VarCollector(); visitPreOrder( root, collector, MATCH_NOT_FUNCTION); return collector.vars.values(); } /** * @return {@code true} if the node an assignment to a prototype property of * some constructor. */ static boolean isPrototypePropertyDeclaration(Node n) { if (!isExprAssign(n)) { return false; } return isPrototypeProperty(n.getFirstChild().getFirstChild()); } static boolean isPrototypeProperty(Node n) { String lhsString = n.getQualifiedName(); if (lhsString == null) { return false; } int prototypeIdx = lhsString.indexOf(".prototype."); return prototypeIdx != -1; } /** * @return The class name part of a qualified prototype name. */ static Node getPrototypeClassName(Node qName) { Node cur = qName; while (cur.isGetProp()) { if (cur.getLastChild().getString().equals("prototype")) { return cur.getFirstChild(); } else { cur = cur.getFirstChild(); } } return null; } /** * @return The string property name part of a qualified prototype name. */ static String getPrototypePropertyName(Node qName) { String qNameStr = qName.getQualifiedName(); int prototypeIdx = qNameStr.lastIndexOf(".prototype."); int memberIndex = prototypeIdx + ".prototype".length() + 1; return qNameStr.substring(memberIndex); } /** * Create a node for an empty result expression: * "void 0" */ static Node newUndefinedNode(Node srcReferenceNode) { Node node = IR.voidNode(IR.number(0)); if (srcReferenceNode != null) { node.copyInformationFromForTree(srcReferenceNode); } return node; } /** * Create a VAR node containing the given name and initial value expression. */ static Node newVarNode(String name, Node value) { Node nodeName = IR.name(name); if (value !=

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>li>The normalize pass renames any variable with the IS_CONSTANT_NAME * annotation and that is initialized to a constant value with * a variable name inlucding $$constant. * <li>Return true here if the variable includes $$constant in its name. * </ol> * * @param node A NAME or STRING node * @return True if the variable is constant */ static boolean isConstantName(Node node) { return node.getBooleanProp(Node.IS_CONSTANT_NAME); } /** Whether the given name is constant by coding convention. */ static boolean isConstantByConvention( CodingConvention convention, Node node, Node parent) { String name = node.getString(); if (parent.isGetProp() && node == parent.getLastChild()) { return convention.isConstantKey(name); } else if (isObjectLitKey(node, parent)) { return convention.isConstantKey(name); } else { return convention.isConstant(name); } } /** * Get the JSDocInfo for a function. */ public static JSDocInfo getFunctionJSDocInfo(Node n) { Preconditions.checkState(n.isFunction()); JSDocInfo fnInfo = n.getJSDocInfo(); if (fnInfo == null && NodeUtil.isFunctionExpression(n)) { // Look for the info on other nodes. Node parent = n.getParent(); if (parent.isAssign()) { // on ASSIGNs fnInfo = parent.getJSDocInfo(); } else if (parent.isName()) { // on var NAME = function() { ... }; fnInfo = parent.getParent().getJSDocInfo(); } } return fnInfo; } /** * @param n The node. * @return The source name property on the node or its ancestors. */ public static String getSourceName(Node n) { String sourceName = null; while (sourceName == null && n != null) { sourceName = n.getSourceFileName(); n = n.getParent(); } return sourceName; } /** * @param n The node. * @return The source name property on the node or its ancestors. */

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> the nth * argument or null if no such parameter exists. */ static Node getArgumentForFunction(Node function, int index) { Preconditions.checkState(function.isFunction()); return getNthSibling( function.getFirstChild().getNext().getFirstChild(), index); } /** * Given the new or call, this returns the nth * argument of the call or null if no such argument exists. */ static Node getArgumentForCallOrNew(Node call, int index) { Preconditions.checkState(isCallOrNew(call)); return getNthSibling( call.getFirstChild().getNext(), index); } private static boolean isToStringMethodCall(Node call) { Node getNode = call.getFirstChild(); if (isGet(getNode)) { Node propNode = getNode.getLastChild(); return propNode.isString() && "toString".equals(propNode.getString()); } return false; } /** Find the best JSDoc for the given node. */ static JSDocInfo getBestJSDocInfo(Node n) { JSDocInfo info = n.getJSDocInfo(); if (info == null) { Node parent = n.getParent(); if (parent == null) { return null; } if (parent.isName()) { return getBestJSDocInfo(parent); } else if (parent.isAssign()) { info = parent.getJSDocInfo(); } else if (isObjectLitKey(parent, parent.getParent())) { info = parent.getJSDocInfo(); } else if (parent.isFunction()) { info = parent.getJSDocInfo(); } else if (parent.isVar() && parent.hasOneChild()) { info = parent.getJSDocInfo(); } } return info; } /** Find the l-value that the given r-value is being assigned to. */ static Node getBestLValue(Node n) { Node parent = n.getParent(); boolean isFunctionDeclaration = isFunctionDeclaration(n); if (isFunctionDeclaration) { return n.getFirstChild(); } else if (parent.isName()) { return parent; } else if (parent.isAssign()) { return parent.getFirstChild();

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> } else if (isObjectLitKey(parent, parent.getParent())) { return parent; } return null; } /** Get the owner of the given l-value node. */ static Node getBestLValueOwner(@Nullable Node lValue) { if (lValue == null || lValue.getParent() == null) { return null; } if (isObjectLitKey(lValue, lValue.getParent())) { return getBestLValue(lValue.getParent()); } else if (isGet(lValue)) { return lValue.getFirstChild(); } return null; } /** Get the name of the given l-value node. */ static String getBestLValueName(@Nullable Node lValue) { if (lValue == null || lValue.getParent() == null) { return null; } if (isObjectLitKey(lValue, lValue.getParent())) { Node owner = getBestLValue(lValue.getParent()); if (owner != null) { String ownerName = getBestLValueName(owner); if (ownerName != null) { return ownerName + "." + getObjectLitKeyName(lValue); } } return null; } return lValue.getQualifiedName(); } /** * @returns false iff the result of the expression is not consumed. */ static boolean isExpressionResultUsed(Node expr) { // TODO(johnlenz): consider sharing some code with trySimpleUnusedResult. Node parent = expr.getParent(); switch (parent.getType()) { case Token.EXPR_RESULT: return false; case Token.HOOK: case Token.AND: case Token.OR: return (expr == parent.getFirstChild()) ? true : isExpressionResultUsed(parent); case Token.COMMA: return (expr == parent.getFirstChild()) ? false : isExpressionResultUsed(parent); case Token.FOR: if (!NodeUtil.isForIn(parent)) { // Only an expression whose result is in the condition part of the // expression is used. return (parent.getChildAtIndex(1) == expr); } break; } return true; } static Node booleanNode(boolean value) { return value ? IR.trueNode()

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>.stop() is missing " + "or Tracer.stop() is not wrapped in a " + "try/finally block. " + "Clearing to avoid memory leak.", new Throwable(trace.toString())); trace.truncateOutstandingEvents(); } trace.startEvent(this); } /** * Create a tracer that isn't summed as part of a total * * @param comment Comment about this tracer */ Tracer(String comment) { this(null, comment); } /** * Construct a tracer whose type is based on the short name of the object * @param object Object to use as type name * @param comment A comment * @return new Tracer. */ static Tracer shortName(Object object, String comment) { if (object == null) { return new Tracer(comment); } return new Tracer(object.getClass().getSimpleName(), comment); } /** * Converts 'v' to a string and pads it with up to 16 spaces for * improved alignment. * @param v The value to convert. * @param digits_column_width The desired with of the string. */ private static String longToPaddedString(long v, int digits_column_width) { int digit_width = numDigits(v); StringBuilder sb = new StringBuilder(); appendSpaces(sb, digits_column_width - digit_width); sb.append(v); return sb.toString(); } /** * Gets the number of digits in an integer when printed in base 10. Assumes * a positive integer. * @param n The value. * @return The number of digits in the string. */ private static int numDigits(long n) { int i = 0; do { i++; n = n / 10; } while (n > 0); return i; } /** * Gets a string of spaces of the length specified. * @param sb The string builder to append to. * @param numSpaces The number of spaces in the string. */ @VisibleForTesting static void appendSpaces(StringBuilder sb, int numSpaces) { if (numSpaces > 16) {

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> logger.warning("Tracer.appendSpaces called with large numSpaces"); // Avoid long loop in case some bug in the caller numSpaces = 16; } while (numSpaces >= 5) { sb.append(" "); numSpaces -= 5; } // We know it's less than 5 now switch (numSpaces) { case 1: sb.append(" "); break; case 2: sb.append(" "); break; case 3: sb.append(" "); break; case 4: sb.append(" "); break; } } /** * Adds a new tracing statistic to a trace * * @param tracingStatistic to enable a run * @return The index of this statistic (for use with stat.extraInfo()), or * -1 if the statistic is not enabled. */ static int addTracingStatistic(TracingStatistic tracingStatistic) { // Check to see if we can enable the tracing statistic before actually // adding it. if (tracingStatistic.enable()) { // No synchronization needed, since this is a copy-on-write array. extraTracingStatistics.add(tracingStatistic); // 99.9% of the time, this will be O(1) and return // extraTracingStatistics.length - 1 return extraTracingStatistics.lastIndexOf(tracingStatistic); } else { return -1; } } /** * For testing purposes only. These removes all current tracers. * Severe errors can occur if there are any active tracers going on * when this is called. * * The test suite uses this to remove any tracers that it has added. */ @VisibleForTesting static void clearTracingStatisticsTestingOnly() { extraTracingStatistics.clear(); } /** * Stop the trace. * This may only be done once and must be done from the same thread * that started it. * @param silence_threshold Traces for time less than silence_threshold * ms will be left out of the trace report. A value of -1 indicates * that the current ThreadTrace silence_threshold should be used. * @return The time that this trace actually ran */ long stop(int

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>, getContextForNonEmptyExpression(context), true); break; default: throw new Error("Unknown type " + type + "\n" + n.toStringTree()); } cc.endSourceMapping(n); } static boolean isSimpleNumber(String s) { int len = s.length(); for (int index = 0; index < len; index++) { char c = s.charAt(index); if (c < '0' || c > '9') { return false; } } return len > 0 && s.charAt(0) != '0'; } static double getSimpleNumber(String s) { if (isSimpleNumber(s)) { try { long l = Long.parseLong(s); if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) { return l; } } catch (NumberFormatException e) { // The number was too long to parse. Fall through to NaN. } } return Double.NaN; } /** * @return Whether the name is an indirect eval. */ private boolean isIndirectEval(Node n) { return n.isName() && "eval".equals(n.getString()) && !n.getBooleanProp(Node.DIRECT_EVAL); } /** * Adds a block or expression, substituting a VOID with an empty statement. * This is used for "for (...);" and "if (...);" type statements. * * @param n The node to print. * @param context The context to determine how the node should be printed. */ private void addNonEmptyStatement( Node n, Context context, boolean allowNonBlockChild) { Node nodeToProcess = n; if (!allowNonBlockChild && !n.isBlock()) { throw new Error("Missing BLOCK child."); } // Strip unneeded blocks, that is blocks with <2 children unless // the CodePrinter specifically wants to keep them. if (n.isBlock()) { int count = getNonEmptyChildCount(n, 2); if (count == 0) { if (cc.shouldPreserveExtraBlocks()) { cc.beginBlock(); cc.endBlock(cc.breakAfterBlockFor(n, context == Context.

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>InList The first in the node list (chained through the next * property). */ void addArrayList(Node firstInList) { boolean lastWasEmpty = false; for (Node n = firstInList; n != null; n = n.getNext()) { if (n != firstInList) { cc.listSeparator(); } addExpr(n, 1); lastWasEmpty = n.isEmpty(); } if (lastWasEmpty) { cc.listSeparator(); } } void addCaseBody(Node caseBody) { cc.beginCaseBody(); add(caseBody); cc.endCaseBody(); } void addAllSiblings(Node n) { for (Node c = n; c != null; c = c.getNext()) { add(c); } } /** Outputs a js string, using the optimal (single/double) quote character */ private void addJsString(Node n) { String s = n.getString(); boolean useSlashV = n.getBooleanProp(Node.SLASH_V); if (useSlashV) { add(jsString(n.getString(), useSlashV)); } else { String cached = ESCAPED_JS_STRINGS.get(s); if (cached == null) { cached = jsString(n.getString(), useSlashV); ESCAPED_JS_STRINGS.put(s, cached); } add(cached); } } private String jsString(String s, boolean useSlashV) { int singleq = 0, doubleq = 0; // could count the quotes and pick the optimal quote character for (int i = 0; i < s.length(); i++) { switch (s.charAt(i)) { case '"': doubleq++; break; case '\'': singleq++; break; } } String doublequote, singlequote; char quote; if (singleq < doubleq) { // more double quotes so escape the single quotes quote = '\''; doublequote = "\""; singlequote = "\\\'"; } else { // more single quotes so escape the doubles quote = '\"'; doublequote = "\\\""; singlequote = "\'";

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> } return strEscape(s, quote, doublequote, singlequote, "\\\\", outputCharsetEncoder, useSlashV); } /** Escapes regular expression */ static String regexpEscape(String s, CharsetEncoder outputCharsetEncoder) { return strEscape(s, '/', "\"", "'", "\\", outputCharsetEncoder, false); } /** * Escapes the given string to a double quoted (") JavaScript/JSON string */ static String escapeToDoubleQuotedJsString(String s) { return strEscape(s, '"', "\\\"", "\'", "\\\\", null, false); } /* If the user doesn't want to specify an output charset encoder, assume they want Latin/ASCII characters only. */ static String regexpEscape(String s) { return regexpEscape(s, null); } /** Helper to escape javascript string as well as regular expression */ private static String strEscape( String s, char quote, String doublequoteEscape, String singlequoteEscape, String backslashEscape, CharsetEncoder outputCharsetEncoder, boolean useSlashV) { StringBuilder sb = new StringBuilder(s.length() + 2); sb.append(quote); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); switch (c) { case '\0': sb.append("\\x00"); break; case '\u000B': if (useSlashV) { sb.append("\\v"); } else { sb.append("\\x0B"); } break; case '\n': sb.append("\\n"); break; case '\r': sb.append("\\r"); break; case '\t': sb.append("\\t"); break; case '\\': sb.append(backslashEscape); break; case '\"': sb.append(doublequoteEscape); break; case '\'': sb.append(singlequoteEscape); break; case '>': // Break --> into --\> or ]]> into ]]\> if (i >= 2 && ((s.charAt(i - 1) == '-' && s.charAt(i - 2) == '-') || (s.charAt(i - 1) == ']' && s.charAt(i - 2

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>) == ']'))) { sb.append("\\>"); } else { sb.append(c); } break; case '<': // Break </script into <\/script final String END_SCRIPT = "/script"; // Break <!-- into <\!-- final String START_COMMENT = "!--"; if (s.regionMatches(true, i + 1, END_SCRIPT, 0, END_SCRIPT.length())) { sb.append("<\\"); } else if (s.regionMatches(false, i + 1, START_COMMENT, 0, START_COMMENT.length())) { sb.append("<\\"); } else { sb.append(c); } break; default: // If we're given an outputCharsetEncoder, then check if the // character can be represented in this character set. if (outputCharsetEncoder != null) { if (outputCharsetEncoder.canEncode(c)) { sb.append(c); } else { // Unicode-escape the character. appendHexJavaScriptRepresentation(sb, c); } } else { // No charsetEncoder provided - pass straight latin characters // through, and escape the rest. Doing the explicit character // check is measurably faster than using the CharsetEncoder. if (c > 0x1f && c < 0x7f) { sb.append(c); } else { // Other characters can be misinterpreted by some js parsers, // or perhaps mangled by proxies along the way, // so we play it safe and unicode escape them. appendHexJavaScriptRepresentation(sb, c); } } } } sb.append(quote); return sb.toString(); } static String identifierEscape(String s) { // First check if escaping is needed at all -- in most cases it isn't. if (NodeUtil.isLatin(s)) { return s; } // Now going through the string to escape non-latin characters if needed. StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); // Identifiers should always go to Latin1/ ASCII characters because // different browser

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> info.isNoShadow()) { return true; } else { return false; } } @Override public boolean equals(Object other) { if (!(other instanceof Var)) { return false; } Var otherVar = (Var) other; return otherVar.nameNode == nameNode; } @Override public int hashCode() { return nameNode.hashCode(); } @Override public String toString() { return "Scope.Var " + name + "{" + type + "}"; } /** Record that this is escaped by an inner scope. */ void markEscaped() { markedEscaped = true; } /** * Whether this is escaped by an inner scope. * Notice that not all scope creators record this information. */ boolean isMarkedEscaped() { return markedEscaped; } } /** * A special subclass of Var used to distinguish "arguments" in the current * scope. */ // TODO(johnlenz): Include this the list of Vars for the scope. public static class Arguments extends Var { Arguments(Scope scope) { super( false, // no inferred "arguments", // always arguments null, // no declaration node // TODO(johnlenz): provide the type of "Arguments". null, // no type info scope, -1, // no variable index null, // input, false, // not a define null // no jsdoc ); } @Override public boolean equals(Object other) { if (!(other instanceof Arguments)) { return false; } Arguments otherVar = (Arguments) other; return otherVar.scope.getRootNode() == scope.getRootNode(); } @Override public int hashCode() { return System.identityHashCode(this); } } /** * Creates a Scope given the parent Scope and the root node of the scope. * @param parent The parent Scope. Cannot be null. * @param rootNode Typically the FUNCTION node. */ Scope(Scope parent, Node rootNode) { Preconditions.checkNotNull(parent); Preconditions.checkArgument(rootNode != parent.rootNode); this.parent = parent; this.rootNode = rootNode; JSType nodeType = root

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>Node.getJSType(); if (nodeType != null && nodeType.isFunctionType()) { thisType = nodeType.toMaybeFunctionType().getTypeOfThis(); } else { thisType = parent.thisType; } this.isBottom = false; this.depth = parent.depth + 1; } /** * Creates a global Scope. * @param rootNode Typically the global BLOCK node. */ Scope(Node rootNode, AbstractCompiler compiler) { this.parent = null; this.rootNode = rootNode; thisType = compiler.getTypeRegistry().getNativeObjectType(GLOBAL_THIS); this.isBottom = false; this.depth = 0; } /** * Creates a empty Scope (bottom of the lattice). * @param rootNode Typically a FUNCTION node or the global BLOCK node. * @param thisType the type of {@code this} in this scope */ Scope(Node rootNode, ObjectType thisType) { this.parent = null; this.rootNode = rootNode; this.thisType = thisType; this.isBottom = true; this.depth = 0; } /** The depth of the scope. The global scope has depth 0. */ int getDepth() { return depth; } /** Whether this is the bottom of the lattice. */ boolean isBottom() { return isBottom; } /** * Gets the container node of the scope. This is typically the FUNCTION * node or the global BLOCK/SCRIPT node. */ @Override public Node getRootNode() { return rootNode; } public Scope getParent() { return parent; } Scope getGlobalScope() { Scope result = this; while (result.getParent() != null) { result = result.getParent(); } return result; } @Override public StaticScope<JSType> getParentScope() { return parent; } /** * Gets the type of {@code this} in the current scope. */ @Override public ObjectType getTypeOfThis() { return thisType; } /** * Declares a variable whose type is inferred. * * @param name name of the variable * @param nameNode the NAME node declaring the

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.rhino.Node; /** * A simple pass to ensure that all AST nodes have line numbers, * an that the line numbers are monotonically increasing. * * @author nicksantos@google.com (Nick Santos) */ class LineNumberCheck implements Callback, CompilerPass { static final DiagnosticType MISSING_LINE_INFO = DiagnosticType.error( "JSC_MISSING_LINE_INFO", "No source location information associated with {0}.\n" + "Most likely a Node has been created with settings the source file " + "and line/column location. Usually this is done using " + "Node.copyInformationFrom and supplying a Node from the source AST."); private final AbstractCompiler compiler; private boolean requiresLineNumbers = false; LineNumberCheck(AbstractCompiler compiler) { this.compiler = compiler; } public void setCheckSubTree(Node root) { requiresLineNumbers = true; NodeTraversal.traverse(compiler, root, this); } @Override public void process(Node externs, Node root) { requiresLineNumbers = false; NodeTraversal.traverse(compiler, root, this); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Each JavaScript file is rooted in a script node, so we'll only // have line number information inside the script node. if (n.isScript()) { requiresLineNumbers

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> Preconditions.checkNotNull(registerFunction); return this.registerFunction == registerFunction; } boolean isGetterFunction() { return registerFunction != null; } String getName() { return name; } String getExpectedTypeName() { return expectedTypeName; } Node createDefaultValueNode() { switch (this) { case REGISTER_BOOLEAN: return IR.falseNode(); case REGISTER_NUMBER: return IR.number(0); case REGISTER_STRING: return IR.string(""); } throw new IllegalStateException(); } } // A map of function name -> TweakFunction. private static final Map<String, TweakFunction> TWEAK_FUNCTIONS_MAP; static { TWEAK_FUNCTIONS_MAP = Maps.newHashMap(); for (TweakFunction func : TweakFunction.values()) { TWEAK_FUNCTIONS_MAP.put(func.getName(), func); } } ProcessTweaks(AbstractCompiler compiler, boolean stripTweaks, Map<String, Node> compilerDefaultValueOverrides) { this.compiler = compiler; this.stripTweaks = stripTweaks; // Having the map sorted is required for the unit tests to be deterministic. this.compilerDefaultValueOverrides = Maps.newTreeMap(); this.compilerDefaultValueOverrides.putAll(compilerDefaultValueOverrides); } @Override public void process(Node externs, Node root) { CollectTweaksResult result = collectTweaks(root); applyCompilerDefaultValueOverrides(result.tweakInfos); boolean changed = false; if (stripTweaks) { changed = stripAllCalls(result.tweakInfos); } else if (!compilerDefaultValueOverrides.isEmpty()) { changed = replaceGetCompilerOverridesCalls(result.getOverridesCalls); } if (changed) { compiler.reportCodeChange(); } } /** * Passes the compiler default value overrides to the JS by replacing calls * to goog.tweak.getCompilerOverrids_ with a map of tweak ID->default value; */ private boolean replaceGetCompilerOverridesCalls( List<TweakFunctionCall> calls) { for (TweakFunctionCall call : calls) { Node callNode = call.callNode; Node obj

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>DefaultValueOverrides( Map<String, TweakInfo> tweakInfos) { for (Entry<String, Node> entry : compilerDefaultValueOverrides.entrySet()) { String tweakId = entry.getKey(); TweakInfo tweakInfo = tweakInfos.get(tweakId); if (tweakInfo == null) { compiler.report(JSError.make(UNKNOWN_TWEAK_WARNING, tweakId)); } else { TweakFunction registerFunc = tweakInfo.registerCall.tweakFunc; Node value = entry.getValue(); if (!registerFunc.isValidNodeType(value.getType())) { compiler.report(JSError.make(INVALID_TWEAK_DEFAULT_VALUE_WARNING, tweakId, registerFunc.getName(), registerFunc.getExpectedTypeName())); } else { tweakInfo.defaultValueNode = value; } } } } /** * Finds all calls to goog.tweak functions and emits warnings/errors if any * of the calls have issues. * @return A map of {@link TweakInfo} structures, keyed by tweak ID. */ private CollectTweaksResult collectTweaks(Node root) { CollectTweaks pass = new CollectTweaks(); NodeTraversal.traverse(compiler, root, pass); Map<String, TweakInfo> tweakInfos = pass.allTweaks; for (TweakInfo tweakInfo: tweakInfos.values()) { tweakInfo.emitAllWarnings(); } return new CollectTweaksResult(tweakInfos, pass.getOverridesCalls); } private final static class CollectTweaksResult { final Map<String, TweakInfo> tweakInfos; final List<TweakFunctionCall> getOverridesCalls; CollectTweaksResult(Map<String, TweakInfo> tweakInfos, List<TweakFunctionCall> getOverridesCalls) { this.tweakInfos = tweakInfos; this.getOverridesCalls = getOverridesCalls; } } /** * Processes all calls to goog.tweak functions. */ private final class CollectTweaks extends AbstractPostOrderCallback { final Map<String, TweakInfo> allTweaks = Maps.newHashMap(); final List<TweakFunctionCall> getOverridesCalls = Lists.newArrayList(); @Override public

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>() && !fnName.isEmpty()) { typeRegistry.declareType(fnName, fnType.getInstanceType()); } return fnType; } private void reportWarning(DiagnosticType warning, String ... args) { compiler.report(JSError.make(sourceName, errorRoot, warning, args)); } private void reportError(DiagnosticType error, String ... args) { compiler.report(JSError.make(sourceName, errorRoot, error, args)); } /** * Determines whether the given jsdoc info declares a function type. */ static boolean isFunctionTypeDeclaration(JSDocInfo info) { return info.getParameterCount() > 0 || info.hasReturnType() || info.hasThisType() || info.isConstructor() || info.isInterface(); } /** * The scope that we should declare this function in, if it needs * to be declared in a scope. Notice that TypedScopeCreator takes * care of most scope-declaring. */ private Scope getScopeDeclaredIn() { int dotIndex = fnName.indexOf("."); if (dotIndex != -1) { String rootVarName = fnName.substring(0, dotIndex); Var rootVar = scope.getVar(rootVarName); if (rootVar != null) { return rootVar.getScope(); } } return scope; } /** * Check whether a type is resolvable in the future * If this has a supertype that hasn't been resolved yet, then we can assume * this type will be ok once the super type resolves. * @param objectType * @return true if objectType is resolvable in the future */ private static boolean hasMoreTagsToResolve(ObjectType objectType) { Preconditions.checkArgument(objectType.isUnknownType()); if (objectType.getImplicitPrototype() != null) { // constructor extends class if (objectType.getImplicitPrototype().isResolved()) { return false; } else { return true; } } else { // interface extends interfaces FunctionType ctor = objectType.getConstructor(); if (ctor != null) { for (ObjectType interfaceType : ctor.getExtendedInterfaces()) { if (!interfaceType.isResolved()) { return

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> re-used for multiple check passes. Returns this for easy chaining. */ CheckGlobalNames injectNamespace(GlobalNamespace namespace) { this.namespace = namespace; return this; } @Override public void process(Node externs, Node root) { // TODO(nicksantos): Let CollapseProperties and CheckGlobalNames // share a namespace. if (namespace == null) { namespace = new GlobalNamespace(compiler, root); } for (Name name : namespace.getNameForest()) { checkDescendantNames(name, name.globalSets + name.localSets > 0); } } /** * Checks to make sure all the descendants of a name are defined if they * are referenced. * * @param name A global name. * @param nameIsDefined If true, {@code name} is defined. Otherwise, it's * undefined, and any references to descendant names should emit warnings. */ private void checkDescendantNames(Name name, boolean nameIsDefined) { if (name.props != null) { for (Name prop : name.props) { // if the ancestor of a property is not defined, then we should emit // warnings for all references to the property. boolean propIsDefined = false; if (nameIsDefined) { // if the ancestor of a property is defined, then let's check that // the property is also explicitly defined if it needs to be. propIsDefined = (!propertyMustBeInitializedByFullName(prop) || prop.globalSets + prop.localSets > 0); } validateName(prop, propIsDefined); checkDescendantNames(prop, propIsDefined); } } } private void validateName(Name name, boolean isDefined) { // If the name is not defined, emit warnings for each reference. While // we're looking through each reference, check all the module dependencies. Ref declaration = name.getDeclaration(); Name parent = name.parent; boolean singleGlobalParentDecl = parent != null && parent.getDeclaration() != null && parent.localSets == 0; JSModuleGraph moduleGraph = compiler.getModuleGraph(); for (Ref ref : name.getRefs()) { if (!isDefined && !isTypedef(ref)) {

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> reportRefToUndefinedName(name, ref); } else if (declaration != null && ref.getModule() != declaration.getModule() && !moduleGraph.dependsOn( ref.getModule(), declaration.getModule())) { reportBadModuleReference(name, ref); } else if (ref.scope.isGlobal() && singleGlobalParentDecl && parent.getDeclaration().preOrderIndex > ref.preOrderIndex) { compiler.report( JSError.make(ref.source.getName(), ref.node, NAME_DEFINED_LATE_WARNING, name.getFullName(), parent.getFullName(), parent.getDeclaration().source.getName(), String.valueOf(parent.getDeclaration().node.getLineno()))); } } } private boolean isTypedef(Ref ref) { // If this is an annotated EXPR-GET, don't do anything. Node parent = ref.node.getParent(); if (parent.isExprResult()) { JSDocInfo info = ref.node.getJSDocInfo(); if (info != null && info.hasTypedefType()) { return true; } } return false; } private void reportBadModuleReference(Name name, Ref ref) { compiler.report( JSError.make(ref.source.getName(), ref.node, STRICT_MODULE_DEP_QNAME, ref.getModule().getName(), name.getDeclaration().getModule().getName(), name.getFullName())); } private void reportRefToUndefinedName(Name name, Ref ref) { // grab the highest undefined ancestor to output in the warning message. while (name.parent != null && name.parent.globalSets + name.parent.localSets == 0) { name = name.parent; } compiler.report( JSError.make(ref.getSourceName(), ref.node, level, UNDEFINED_NAME_WARNING, name.getFullName())); } /** * Checks whether the given name is a property, and whether that property * must be initialized with its full qualified name. */ private static boolean propertyMustBeInitializedByFullName(Name name) { // If an object literal in the global namespace is never aliased, // then all of its properties must be defined using its

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>classRelationship(type, subclass, superclass); } } return null; } /** * Determines whether the given node is a class-defining name, like * "inherits" or "mixin." * @return The type of class-defining name, or null. */ private SubclassType typeofClassDefiningName(Node callName) { // Check if the method name matches one of the class-defining methods. String methodName = null; if (callName.isGetProp()) { methodName = callName.getLastChild().getString(); } else if (callName.isName()) { String name = callName.getString(); int dollarIndex = name.lastIndexOf('$'); if (dollarIndex != -1) { methodName = name.substring(dollarIndex + 1); } } if (methodName != null) { if (methodName.equals("inherits")) { return SubclassType.INHERITS; } else if (methodName.equals("mixin")) { return SubclassType.MIXIN; } } return null; } @Override public boolean isSuperClassReference(String propertyName) { return "superClass_".equals(propertyName); } /** * Given a qualified name node, returns whether "prototype" is at the end. * For example: * a.b.c => false * a.b.c.prototype => true */ private boolean endsWithPrototype(Node qualifiedName) { return qualifiedName.isGetProp() && qualifiedName.getLastChild().getString().equals("prototype"); } /** * Exctracts X from goog.provide('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfProvide(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.provide"); } /** * Exctracts X from goog.require('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfRequire(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.require"); } private static String extract

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>(FunctionType functionType, FunctionType getterType, ObjectType objectType) { functionType.defineDeclaredProperty("getInstance", getterType, functionType.getSource()); functionType.defineDeclaredProperty("instance_", objectType, functionType.getSource()); } @Override public String getGlobalObject() { return "goog.global"; } private final Set<String> propertyTestFunctions = ImmutableSet.of( "goog.isDef", "goog.isNull", "goog.isDefAndNotNull", "goog.isString", "goog.isNumber", "goog.isBoolean", "goog.isFunction", "goog.isArray", "goog.isObject"); @Override public boolean isPropertyTestFunction(Node call) { Preconditions.checkArgument(call.isCall()); return propertyTestFunctions.contains( call.getFirstChild().getQualifiedName()); } @Override public ObjectLiteralCast getObjectLiteralCast(NodeTraversal t, Node callNode) { Preconditions.checkArgument(callNode.isCall()); Node callName = callNode.getFirstChild(); if (!"goog.reflect.object".equals(callName.getQualifiedName()) || callNode.getChildCount() != 3) { return null; } Node typeNode = callName.getNext(); if (!typeNode.isQualifiedName()) { return null; } Node objectNode = typeNode.getNext(); if (!objectNode.isObjectLit()) { // TODO(johnlenz): The coding convention should not be performing checks. t.getCompiler().report(JSError.make(t.getSourceName(), callNode, OBJECTLIT_EXPECTED)); return null; } return new ObjectLiteralCast(typeNode.getQualifiedName(), typeNode.getNext()); } @Override public boolean isOptionalParameter(Node parameter) { return false; } @Override public boolean isVarArgsParameter(Node parameter) { return false; } @Override public boolean isPrivate(String name) { return false; } @Override public Collection<AssertionFunctionSpec> getAssertionFunctions() { return ImmutableList.<AssertionFunctionSpec>of( new AssertionFunctionSpec("goog.asserts.assert"), new AssertionFunctionSpec("goog.asserts.assertNumber", JSTypeNative.NUMBER_

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> SimpleSourceFile((String) value, false)); return; } removeProp(propType); if (value != null) { propListHead = createProp(propType, value, propListHead); } } public void putBooleanProp(int propType, boolean value) { putIntProp(propType, value ? 1 : 0); } public void putIntProp(int propType, int value) { removeProp(propType); if (value != 0) { propListHead = createProp(propType, value, propListHead); } } PropListItem createProp(int propType, Object value, PropListItem next) { return new ObjectPropListItem(propType, value, next); } PropListItem createProp(int propType, int value, PropListItem next) { return new IntPropListItem(propType, value, next); } // Gets all the property types, in sorted order. private int[] getSortedPropTypes() { int count = 0; for (PropListItem x = propListHead; x != null; x = x.getNext()) { count++; } int[] keys = new int[count]; for (PropListItem x = propListHead; x != null; x = x.getNext()) { count--; keys[count] = x.getType(); } Arrays.sort(keys); return keys; } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public double getDouble() throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a number node"); } } /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */ public void setDouble(double s) throws UnsupportedOperationException { if (this.getType() == Token.NUMBER) { throw new IllegalStateException( "Number node not created with Node.newNumber"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */ public String getString() throws UnsupportedOperationException { if (

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } /** Can only be called when node has String context. */ public void setString(String s) throws UnsupportedOperationException { if (this.getType() == Token.STRING) { throw new IllegalStateException( "String node not created with Node.newString"); } else { throw new UnsupportedOperationException(this + " is not a string node"); } } @Override public String toString() { return toString(true, true, true); } public String toString( boolean printSource, boolean printAnnotations, boolean printType) { StringBuilder sb = new StringBuilder(); toString(sb, printSource, printAnnotations, printType); return sb.toString(); } private void toString( StringBuilder sb, boolean printSource, boolean printAnnotations, boolean printType) { sb.append(Token.name(type)); if (this instanceof StringNode) { sb.append(' '); sb.append(getString()); } else if (type == Token.FUNCTION) { sb.append(' '); // In the case of JsDoc trees, the first child is often not a string // which causes exceptions to be thrown when calling toString or // toStringTree. if (first == null || first.getType() != Token.NAME) { sb.append("<invalid>"); } else { sb.append(first.getString()); } } else if (type == Token.NUMBER) { sb.append(' '); sb.append(getDouble()); } if (printSource) { int lineno = getLineno(); if (lineno != -1) { sb.append(' '); sb.append(lineno); } } if (printAnnotations) { int[] keys = getSortedPropTypes(); for (int i = 0; i < keys.length; i++) { int type = keys[i]; PropListItem x = lookupProperty(type); sb.append(" ["); sb.append(propToString(type)); sb.append(": "); String value; switch (type) { default: value = x.toString

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>(); break; } sb.append(value); sb.append(']'); } } if (printType) { if (jsType != null) { String jsTypeString = jsType.toString(); if (jsTypeString != null) { sb.append(" : "); sb.append(jsTypeString); } } } } public String toStringTree() { return toStringTreeImpl(); } private String toStringTreeImpl() { try { StringBuilder s = new StringBuilder(); appendStringTree(s); return s.toString(); } catch (IOException e) { throw new RuntimeException("Should not happen\n" + e); } } public void appendStringTree(Appendable appendable) throws IOException { toStringTreeHelper(this, 0, appendable); } private static void toStringTreeHelper(Node n, int level, Appendable sb) throws IOException { for (int i = 0; i != level; ++i) { sb.append(" "); } sb.append(n.toString()); sb.append('\n'); for (Node cursor = n.getFirstChild(); cursor != null; cursor = cursor.getNext()) { toStringTreeHelper(cursor, level + 1, sb); } } int type; // type of the node; Token.NAME for example Node next; // next sibling private Node first; // first element of a linked list of children private Node last; // last element of a linked list of children /** * Linked list of properties. Since vast majority of nodes would have * no more then 2 properties, linked list saves memory and provides * fast lookup. If this does not holds, propListHead can be replaced * by UintMap. */ private PropListItem propListHead; /** * COLUMN_BITS represents how many of the lower-order bits of * sourcePosition are reserved for storing the column number. * Bits above these store the line number. * This gives us decent position information for everything except * files already passed through a minimizer, where lines might * be longer than 4096 characters. */ public static final int COLUMN_BITS = 12; /** * MAX_COLUMN_NUMBER represents the

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> int post2 = node.getIntProp(INCRDECR_PROP); if (post1 != post2) { return false; } } else if (type == Token.STRING) { int quoted1 = this.getIntProp(QUOTED_PROP); int quoted2 = node.getIntProp(QUOTED_PROP); if (quoted1 != quoted2) { return false; } int slashV1 = this.getIntProp(SLASH_V); int slashV2 = node.getIntProp(SLASH_V); if (slashV1 != slashV2) { return false; } } else if (type == Token.CALL) { if (this.getBooleanProp(FREE_CALL) != node.getBooleanProp(FREE_CALL)) { return false; } } if (recurse) { Node n, n2; for (n = first, n2 = node.first; n != null; n = n.next, n2 = n2.next) { if (!n.isEquivalentTo(n2, compareJsType, true)) { return false; } } } return true; } /** * This function takes a set of GETPROP nodes and produces a string that is * each property separated by dots. If the node ultimately under the left * sub-tree is not a simple name, this is not a valid qualified name. * * @return a null if this is not a qualified name, or a dot-separated string * of the name and properties. */ public String getQualifiedName() { if (type == Token.NAME) { return getString(); } else if (type == Token.GETPROP) { String left = getFirstChild().getQualifiedName(); if (left == null) { return null; } return left + "." + getLastChild().getString(); } else if (type == Token.THIS) { return "this"; } else { return null; } } /** * Returns whether a node corresponds to a simple or a qualified name, such as * <code>x</code> or <code>a.b.c</code> or <code>this.a</code>. */ public boolean isQualifiedName

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>() { switch (getType()) { case Token.NAME: case Token.THIS: return true; case Token.GETPROP: return getFirstChild().isQualifiedName(); default: return false; } } /** * Returns whether a node corresponds to a simple or a qualified name without * a "this" reference, such as <code>a.b.c</code>, but not <code>this.a</code> * . */ public boolean isUnscopedQualifiedName() { switch (getType()) { case Token.NAME: return true; case Token.GETPROP: return getFirstChild().isUnscopedQualifiedName(); default: return false; } } // ========================================================================== // Mutators /** * Removes this node from its parent. Equivalent to: * node.getParent().removeChild(); */ public Node detachFromParent() { Preconditions.checkState(parent != null); parent.removeChild(this); return this; } /** * Removes the first child of Node. Equivalent to: * node.removeChild(node.getFirstChild()); * * @return The removed Node. */ public Node removeFirstChild() { Node child = first; if (child != null) { removeChild(child); } return child; } /** * @return A Node that is the head of the list of children. */ public Node removeChildren() { Node children = first; for (Node child = first; child != null; child = child.getNext()) { child.parent = null; } first = null; last = null; return children; } /** * Removes all children from this node and isolates the children from each * other. */ public void detachChildren() { for (Node child = first; child != null;) { Node nextChild = child.getNext(); child.parent = null; child.next = null; child = nextChild; } first = null; last = null; } public Node removeChildAfter(Node prev) { Preconditions.checkArgument(prev.parent == this, "prev is not a child of this node."); Preconditions.checkArgument(prev.next != null, "

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>/* * Copyright 2008 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.collect.Maps; import com.google.javascript.jscomp.CheckLevel; import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.JSDocInfo.Visibility; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import java.util.Map; /** * Insures '@constructor X' has a 'goog.provide("X")' . * */ class CheckProvides implements HotSwapCompilerPass { private final AbstractCompiler compiler; private final CheckLevel checkLevel; private final CodingConvention codingConvention; static final DiagnosticType MISSING_PROVIDE_WARNING = DiagnosticType.disabled( "JSC_MISSING_PROVIDE", "missing goog.provide(''{0}'')"); CheckProvides(AbstractCompiler compiler, CheckLevel checkLevel) { this.compiler = compiler; this.checkLevel = checkLevel; this.codingConvention = compiler.getCodingConvention(); } @Override public void process(Node externs, Node root) { hotSwapScript(root, null); } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { CheckProvidesCallback callback = new CheckProvidesCallback(codingConvention); new NodeTraversal(compiler, callback).traverse(scriptRoot); } private class CheckProvidesCallback extends AbstractShallowCallback { private final

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> Map<String, Node> provides = Maps.newHashMap(); private final Map<String, Node> ctors = Maps.newHashMap(); private final CodingConvention convention; CheckProvidesCallback(CodingConvention convention){ this.convention = convention; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.CALL: String providedClassName = codingConvention.extractClassNameIfProvide(n, parent); if (providedClassName != null) { provides.put(providedClassName, n); } break; case Token.FUNCTION: visitFunctionNode(n, parent); break; case Token.SCRIPT: visitScriptNode(t, n); } } private void visitFunctionNode(Node n, Node parent) { Node name = null; JSDocInfo info = parent.getJSDocInfo(); if (info != null && info.isConstructor()) { name = parent.getFirstChild(); } else { // look to the child, maybe it's a named function info = n.getJSDocInfo(); if (info != null && info.isConstructor()) { name = n.getFirstChild(); } } if (name != null && name.isQualifiedName()) { String qualifiedName = name.getQualifiedName(); if (!this.convention.isPrivate(qualifiedName)) { Visibility visibility = info.getVisibility(); if (!visibility.equals(JSDocInfo.Visibility.PRIVATE)) { ctors.put(qualifiedName, name); } } } } private void visitScriptNode(NodeTraversal t, Node n) { for (Map.Entry<String, Node> ctorEntry : ctors.entrySet()) { if (!provides.containsKey(ctorEntry.getKey())) { compiler.report( t.makeError(ctorEntry.getValue(), checkLevel, MISSING_PROVIDE_WARNING, ctorEntry.getKey())); } } provides.clear(); ctors.clear(); } } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>Native.NULL_TYPE)); register(getNativeType(JSTypeNative.NULL_TYPE), "Null"); register(getNativeType(JSTypeNative.NUMBER_OBJECT_TYPE)); register(getNativeType(JSTypeNative.NUMBER_TYPE)); register(getNativeType(JSTypeNative.OBJECT_TYPE)); register(getNativeType(JSTypeNative.ERROR_TYPE)); register(getNativeType(JSTypeNative.URI_ERROR_TYPE)); register(getNativeType(JSTypeNative.EVAL_ERROR_TYPE)); register(getNativeType(JSTypeNative.TYPE_ERROR_TYPE)); register(getNativeType(JSTypeNative.RANGE_ERROR_TYPE)); register(getNativeType(JSTypeNative.REFERENCE_ERROR_TYPE)); register(getNativeType(JSTypeNative.SYNTAX_ERROR_TYPE)); register(getNativeType(JSTypeNative.REGEXP_TYPE)); register(getNativeType(JSTypeNative.STRING_OBJECT_TYPE)); register(getNativeType(JSTypeNative.STRING_TYPE)); register(getNativeType(JSTypeNative.VOID_TYPE)); register(getNativeType(JSTypeNative.VOID_TYPE), "Undefined"); register(getNativeType(JSTypeNative.VOID_TYPE), "void"); register(getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), "Function"); } private void register(JSType type) { register(type, type.toString()); } private void register(JSType type, String name) { namesToTypes.put(name, type); // Add all the namespaces in which this name lives. while (name.indexOf('.') > 0) { name = name.substring(0, name.lastIndexOf('.')); namespaces.add(name); } } private void registerNativeType(JSTypeNative typeId, JSType type) { nativeTypes[typeId.ordinal()] = type; } /** * Tells the type system that {@code owner} may have a property named * {@code propertyName}. This allows the registry to keep track of what * types a property is defined upon. * * This is NOT the same as saying that {@code owner} must have a property * named type. ObjectType#hasProperty attempts

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> new FunctionBuilder(this) .withParams(paramBuilder) .withReturnType(returnType) .withTypeOfThis(thisType) .setIsConstructor(isConstructor) .build(); } throw new IllegalStateException( "Unexpected node in type expression: " + n.toString()); } /** * Creates a RecordType from the nodes representing said record type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ private JSType createRecordTypeFromNodes(Node n, String sourceName, StaticScope<JSType> scope) { RecordTypeBuilder builder = new RecordTypeBuilder(this); // For each of the fields in the record type. for (Node fieldTypeNode = n.getFirstChild(); fieldTypeNode != null; fieldTypeNode = fieldTypeNode.getNext()) { // Get the property's name. Node fieldNameNode = fieldTypeNode; boolean hasType = false; if (fieldTypeNode.getType() == Token.COLON) { fieldNameNode = fieldTypeNode.getFirstChild(); hasType = true; } String fieldName = fieldNameNode.getString(); // TODO(user): Move this into the lexer/parser. // Remove the string literal characters around a field name, // if any. if (fieldName.startsWith("'") || fieldName.startsWith("\"")) { fieldName = fieldName.substring(1, fieldName.length() - 1); } // Get the property's type. JSType fieldType = null; if (hasType) { // We have a declared type. fieldType = createFromTypeNodesInternal( fieldTypeNode.getLastChild(), sourceName, scope); } else { // Otherwise, the type is UNKNOWN. fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE); } // Add the property to the record. if (builder.addProperty(fieldName, fieldType, fieldNameNode) == null) { // Duplicate field name, warning and skip reporter.warning( "Duplicate record field " + fieldName, sourceName, n.getLineno(), fieldNameNode.getCharno()); } } return builder.build(); } /** * Sets the template type name. */ public void setTemplateTypeName

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>.resolve(typeParsingErrorReporter, scope)); } } TypedScopeCreator(AbstractCompiler compiler) { this(compiler, compiler.getCodingConvention()); } TypedScopeCreator(AbstractCompiler compiler, CodingConvention codingConvention) { this.compiler = compiler; this.validator = compiler.getTypeValidator(); this.codingConvention = codingConvention; this.typeRegistry = compiler.getTypeRegistry(); this.typeParsingErrorReporter = typeRegistry.getErrorReporter(); } /** * Creates a scope with all types declared. Declares newly discovered types * and type properties in the type registry. */ @Override public Scope createScope(Node root, Scope parent) { // Constructing the global scope is very different than constructing // inner scopes, because only global scopes can contain named classes that // show up in the type registry. Scope newScope = null; AbstractScopeBuilder scopeBuilder = null; if (parent == null) { // Run a first-order analysis over the syntax tree. (new FirstOrderFunctionAnalyzer(compiler, functionAnalysisResults)) .process(root.getFirstChild(), root.getLastChild()); // Find all the classes in the global scope. newScope = createInitialScope(root); GlobalScopeBuilder globalScopeBuilder = new GlobalScopeBuilder(newScope); scopeBuilder = globalScopeBuilder; NodeTraversal.traverse(compiler, root, scopeBuilder); } else { newScope = new Scope(parent, root); LocalScopeBuilder localScopeBuilder = new LocalScopeBuilder(newScope); scopeBuilder = localScopeBuilder; localScopeBuilder.build(); } scopeBuilder.resolveStubDeclarations(); scopeBuilder.resolveTypes(); // Gather the properties in each function that we found in the // global scope, if that function has a @this type that we can // build properties on. for (Node functionNode : scopeBuilder.nonExternFunctions) { JSType type = functionNode.getJSType(); if (type != null && type.isFunctionType()) { FunctionType fnType = type.toMaybeFunctionType(); ObjectType fnThisType = fnType.getTypeOfThis(); if (!fnThisType.isUnknownType()) { NodeTraversal.traverse(compiler, functionNode.getLastChild(), scopeBuilder.new CollectProperties

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> scope. This scope contains native binding such as * {@code Object}, {@code Date}, etc. */ @VisibleForTesting Scope createInitialScope(Node root) { NodeTraversal.traverse( compiler, root, new DiscoverEnumsAndTypedefs(typeRegistry)); Scope s = new Scope(root, compiler); declareNativeFunctionType(s, ARRAY_FUNCTION_TYPE); declareNativeFunctionType(s, BOOLEAN_OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, DATE_FUNCTION_TYPE); declareNativeFunctionType(s, ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, EVAL_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, FUNCTION_FUNCTION_TYPE); declareNativeFunctionType(s, NUMBER_OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, RANGE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, REFERENCE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, REGEXP_FUNCTION_TYPE); declareNativeFunctionType(s, STRING_OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, SYNTAX_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, TYPE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, URI_ERROR_FUNCTION_TYPE); declareNativeValueType(s, "undefined", VOID_TYPE); // ActiveXObject is unqiuely special, because it can be used to construct // any type (the type that it creates is related to the arguments you // pass to it). declareNativeValueType(s, "ActiveXObject", NO_OBJECT_TYPE); return s; } private void declareNativeFunctionType(Scope scope, JSTypeNative tId) { FunctionType t = typeRegistry.getNativeFunctionType(tId); declareNativeType(scope, t.getInstanceType().getReferenceName(), t); declareNativeType( scope, t.getPrototype().getReferenceName(), t.getPrototype()); } private void declareNativeValueType(Scope scope, String name, JSTypeNative tId) { declareNativeType(scope, name, typeRegistry.getNativeType(tId)); } private void declareNativeType(Scope scope, String name, JSType t) { scope.declare(name, null, t

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>, null, false); } private static class DiscoverEnumsAndTypedefs extends AbstractShallowStatementCallback { private final JSTypeRegistry registry; DiscoverEnumsAndTypedefs(JSTypeRegistry registry) { this.registry = registry; } @Override public void visit(NodeTraversal t, Node node, Node parent) { Node nameNode = null; switch (node.getType()) { case Token.VAR: for (Node child = node.getFirstChild(); child != null; child = child.getNext()) { identifyNameNode( child, child.getFirstChild(), NodeUtil.getBestJSDocInfo(child)); } break; case Token.EXPR_RESULT: Node firstChild = node.getFirstChild(); if (firstChild.isAssign()) { identifyNameNode( firstChild.getFirstChild(), firstChild.getLastChild(), firstChild.getJSDocInfo()); } else { identifyNameNode( firstChild, null, firstChild.getJSDocInfo()); } break; } } private void identifyNameNode( Node nameNode, Node valueNode, JSDocInfo info) { if (nameNode.isQualifiedName()) { if (info != null) { if (info.hasEnumParameterType()) { registry.identifyNonNullableName(nameNode.getQualifiedName()); } else if (info.hasTypedefType()) { registry.identifyNonNullableName(nameNode.getQualifiedName()); } } } } } private JSType getNativeType(JSTypeNative nativeType) { return typeRegistry.getNativeType(nativeType); } private abstract class AbstractScopeBuilder implements NodeTraversal.Callback { /** * The scope that we're builidng. */ final Scope scope; private final List<DeferredSetType> deferredSetTypes = Lists.newArrayList(); /** * Functions that we found in the global scope and not in externs. */ private final List<Node> nonExternFunctions = Lists.newArrayList(); /** * Type-less stubs. * * If at the end of traversal, we still don't have types for these * stubs, then we should declare UNKNOWN types. */ private final List<StubDeclaration>

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> stubDeclarations = Lists.newArrayList(); /** * The current source file that we're in. */ private String sourceName = null; /** * The InputId of the current node. */ private InputId inputId; private AbstractScopeBuilder(Scope scope) { this.scope = scope; } void setDeferredType(Node node, JSType type) { deferredSetTypes.add(new DeferredSetType(node, type)); } void resolveTypes() { // Resolve types and attach them to nodes. for (DeferredSetType deferred : deferredSetTypes) { deferred.resolve(scope); } // Resolve types and attach them to scope slots. Iterator<Var> vars = scope.getVars(); while (vars.hasNext()) { vars.next().resolveType(typeParsingErrorReporter); } // Tell the type registry that any remaining types // are unknown. typeRegistry.resolveTypesInScope(scope); } @Override public final boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { inputId = t.getInputId(); if (n.isFunction() || n.isScript()) { Preconditions.checkNotNull(inputId); sourceName = NodeUtil.getSourceName(n); } // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. boolean descend = parent == null || !parent.isFunction() || n == parent.getFirstChild() || parent == scope.getRootNode(); if (descend) { // Handle hoisted functions on pre-order traversal, so that they // get hit before other things in the scope. if (NodeUtil.isStatementParent(n)) { for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (NodeUtil.isHoistedFunctionDeclaration(child)) { defineFunctionLiteral(child, n); } } } } return descend; } @Override public void visit(NodeTraversal t, Node n, Node parent) { inputId = t.getInputId(); attachLiteralTypes(t, n); switch (n.getType()) { case Token.CALL: checkForClassDefining

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>Calls(t, n, parent); checkForCallingConventionDefiningCalls(n, delegateCallingConventions); break; case Token.FUNCTION: if (t.getInput() == null || !t.getInput().isExtern()) { nonExternFunctions.add(n); } // Hoisted functions are handled during pre-traversal. if (!NodeUtil.isHoistedFunctionDeclaration(n)) { defineFunctionLiteral(n, parent); } break; case Token.ASSIGN: // Handle initialization of properties. Node firstChild = n.getFirstChild(); if (firstChild.isGetProp() && firstChild.isQualifiedName()) { maybeDeclareQualifiedName(t, n.getJSDocInfo(), firstChild, n, firstChild.getNext()); } break; case Token.CATCH: defineCatch(n, parent); break; case Token.VAR: defineVar(n, parent); break; case Token.GETPROP: // Handle stubbed properties. if (parent.isExprResult() && n.isQualifiedName()) { maybeDeclareQualifiedName(t, n.getJSDocInfo(), n, parent, null); } break; } } private void attachLiteralTypes(NodeTraversal t, Node n) { switch (n.getType()) { case Token.NULL: n.setJSType(getNativeType(NULL_TYPE)); break; case Token.VOID: n.setJSType(getNativeType(VOID_TYPE)); break; case Token.STRING: // Defer keys to the Token.OBJECTLIT case if (!NodeUtil.isObjectLitKey(n, n.getParent())) { n.setJSType(getNativeType(STRING_TYPE)); } break; case Token.NUMBER: n.setJSType(getNativeType(NUMBER_TYPE)); break; case Token.TRUE: case Token.FALSE: n.setJSType(getNativeType(BOOLEAN_TYPE)); break; case Token.REGEXP: n.setJSType(getNativeType(REGEXP_TYPE)); break; case Token.OBJECTLIT: defineObjectLiteral(t, n); break; // NOTE(nicksantos): If we

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> ever support Array tuples, // we will need to put ARRAYLIT here as well. } } private void defineObjectLiteral(NodeTraversal t, Node objectLit) { // Handle the @lends annotation. JSType type = null; JSDocInfo info = objectLit.getJSDocInfo(); if (info != null && info.getLendsName() != null) { String lendsName = info.getLendsName(); Var lendsVar = scope.getVar(lendsName); if (lendsVar == null) { compiler.report( JSError.make(sourceName, objectLit, UNKNOWN_LENDS, lendsName)); } else { type = lendsVar.getType(); if (type == null) { type = typeRegistry.getNativeType(UNKNOWN_TYPE); } if (!type.isSubtype(typeRegistry.getNativeType(OBJECT_TYPE))) { compiler.report( JSError.make(sourceName, objectLit, LENDS_ON_NON_OBJECT, lendsName, type.toString())); type = null; } else { objectLit.setJSType(type); } } } info = NodeUtil.getBestJSDocInfo(objectLit); Node lValue = NodeUtil.getBestLValue(objectLit); String lValueName = NodeUtil.getBestLValueName(lValue); boolean createdEnumType = false; if (info != null && info.hasEnumParameterType()) { type = createEnumTypeFromNodes(objectLit, lValueName, info, lValue); createdEnumType = true; } if (type == null) { type = typeRegistry.createAnonymousObjectType(); } setDeferredType(objectLit, type); // If this is an enum, the properties were already taken care of above. processObjectLitProperties( t, objectLit, ObjectType.cast(objectLit.getJSType()), !createdEnumType); } /** * Process an object literal and all the types on it. * @param objLit The OBJECTLIT node. * @param objLitType The type of the OBJECTLIT node. This might be a named * type, because of the lends annotation.

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> * @param declareOnOwner If true, declare properties on the objLitType as * well. If false, the caller should take crae of this. */ void processObjectLitProperties( NodeTraversal t, Node objLit, ObjectType objLitType, boolean declareOnOwner) { for (Node keyNode = objLit.getFirstChild(); keyNode != null; keyNode = keyNode.getNext()) { Node value = keyNode.getFirstChild(); String memberName = NodeUtil.getObjectLitKeyName(keyNode); JSDocInfo info = keyNode.getJSDocInfo(); JSType valueType = getDeclaredType(t.getSourceName(), info, keyNode, value); JSType keyType = objLitType.isEnumType() ? objLitType.toMaybeEnumType().getElementsType() : NodeUtil.getObjectLitKeyTypeFromValueType(keyNode, valueType); // Try to declare this property in the current scope if it // has an authoritative name. String qualifiedName = NodeUtil.getBestLValueName(keyNode); if (qualifiedName != null) { boolean inferred = keyType == null; defineSlot(keyNode, objLit, qualifiedName, keyType, inferred); } else if (keyType != null) { setDeferredType(keyNode, keyType); } if (keyType != null && objLitType != null && declareOnOwner) { // Declare this property on its object literal. boolean isExtern = t.getInput() != null && t.getInput().isExtern(); objLitType.defineDeclaredProperty(memberName, keyType, keyNode); } } } /** * Returns the type specified in a JSDoc annotation near a GETPROP or NAME. * * Extracts type information from either the {@code @type} tag or from * the {@code @return} and {@code @param} tags. */ private JSType getDeclaredTypeInAnnotation(String sourceName, Node node, JSDocInfo info) { JSType jsType = null; Node objNode = node.isGetProp() ? node.getFirstChild() : NodeUtil.isObjectLitKey(node, node.getParent()) ? node.getParent() : null;

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> if (info != null) { if (info.hasType()) { jsType = info.getType().evaluate(scope, typeRegistry); } else if (FunctionTypeBuilder.isFunctionTypeDeclaration(info)) { String fnName = node.getQualifiedName(); jsType = createFunctionTypeFromNodes( null, fnName, info, node); } } return jsType; } /** * Asserts that it's ok to define this node's name. * The node should have a source name and be of the specified type. */ void assertDefinitionNode(Node n, int type) { Preconditions.checkState(sourceName != null); Preconditions.checkState(n.getType() == type); } /** * Defines a catch parameter. */ void defineCatch(Node n, Node parent) { assertDefinitionNode(n, Token.CATCH); Node catchName = n.getFirstChild(); defineSlot(catchName, n, null); } /** * Defines a VAR initialization. */ void defineVar(Node n, Node parent) { assertDefinitionNode(n, Token.VAR); JSDocInfo info = n.getJSDocInfo(); if (n.hasMoreThanOneChild()) { if (info != null) { // multiple children compiler.report(JSError.make(sourceName, n, MULTIPLE_VAR_DEF)); } for (Node name : n.children()) { defineName(name, n, parent, name.getJSDocInfo()); } } else { Node name = n.getFirstChild(); defineName(name, n, parent, (info != null) ? info : name.getJSDocInfo()); } } /** * Defines a function literal. */ void defineFunctionLiteral(Node n, Node parent) { assertDefinitionNode(n, Token.FUNCTION); // Determine the name and JSDocInfo and lvalue for the function. // Any of these may be null. Node lValue = NodeUtil.getBestLValue(n); JSDocInfo info = NodeUtil.getBestJSDocInfo(n); String functionName = NodeUtil.getBestLValueName(lValue); FunctionType functionType = createFunctionType

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>FromNodes(n, functionName, info, lValue); // Assigning the function type to the function node setDeferredType(n, functionType); // Declare this symbol in the current scope iff it's a function // declaration. Otherwise, the declaration will happen in other // code paths. if (NodeUtil.isFunctionDeclaration(n)) { defineSlot(n.getFirstChild(), n, functionType); } } /** * Defines a variable based on the {@link Token#NAME} node passed. * @param name The {@link Token#NAME} node. * @param var The parent of the {@code name} node, which must be a * {@link Token#VAR} node. * @param parent {@code var}'s parent. * @param info the {@link JSDocInfo} information relating to this * {@code name} node. */ private void defineName(Node name, Node var, Node parent, JSDocInfo info) { Node value = name.getFirstChild(); // variable's type JSType type = getDeclaredType(sourceName, info, name, value); if (type == null) { // The variable's type will be inferred. type = name.isFromExterns() ? getNativeType(UNKNOWN_TYPE) : null; } defineSlot(name, var, type); } /** * If a variable is assigned a function literal in the global scope, * make that a declared type (even if there's no doc info). * There's only one exception to this rule: * if the return type is inferred, and we're in a local * scope, we should assume the whole function is inferred. */ private boolean shouldUseFunctionLiteralType( FunctionType type, JSDocInfo info, Node lValue) { if (info != null) { return true; } if (lValue != null && NodeUtil.isObjectLitKey(lValue, lValue.getParent())) { return false; } return scope.isGlobal() || !type.isReturnTypeInferred(); } /** * Creates a new function type, based on the given nodes. * * This handles two cases that are semantically very different, but * are not mutually

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> exclusive: * - A function literal that needs a type attached to it. * - An assignment expression with function-type info in the jsdoc. * * All parameters are optional, and we will do the best we can to create * a function type. * * This function will always create a function type, so only call it if * you're sure that's what you want. * * @param rValue The function node. * @param name the function's name * @param info the {@link JSDocInfo} attached to the function definition * @param lvalueNode The node where this function is being * assigned. For example, {@code A.prototype.foo = ...} would be used to * determine that this function is a method of A.prototype. May be * null to indicate that this is not being assigned to a qualified name. */ private FunctionType createFunctionTypeFromNodes( @Nullable Node rValue, @Nullable String name, @Nullable JSDocInfo info, @Nullable Node lvalueNode) { FunctionType functionType = null; // Global ctor aliases should be registered with the type registry. if (rValue != null && rValue.isQualifiedName() && scope.isGlobal()) { Var var = scope.getVar(rValue.getQualifiedName()); if (var != null && var.getType() != null && var.getType().isFunctionType()) { FunctionType aliasedType = var.getType().toMaybeFunctionType(); if ((aliasedType.isConstructor() || aliasedType.isInterface()) && !aliasedType.isNativeObjectType()) { functionType = aliasedType; if (name != null && scope.isGlobal()) { typeRegistry.declareType(name, functionType.getInstanceType()); } } } } if (functionType == null) { Node errorRoot = rValue == null ? lvalueNode : rValue; boolean isFnLiteral = rValue != null && rValue.isFunction(); Node fnRoot = isFnLiteral ? rValue : null; Node parametersNode = isFnLiteral ? rValue.getFirstChild().getNext() : null; Node fnBlock = isFnLiteral ? parametersNode.getNext() : null; if (info != null && info

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>.hasType()) { JSType type = info.getType().evaluate(scope, typeRegistry); // Known to be not null since we have the FUNCTION token there. type = type.restrictByNotNullOrUndefined(); if (type.isFunctionType()) { functionType = type.toMaybeFunctionType(); functionType.setJSDocInfo(info); } } if (functionType == null) { // Find the type of any overridden function. Node ownerNode = NodeUtil.getBestLValueOwner(lvalueNode); String ownerName = NodeUtil.getBestLValueName(ownerNode); Var ownerVar = null; String propName = null; ObjectType ownerType = null; if (ownerName != null) { ownerVar = scope.getVar(ownerName); if (ownerVar != null) { ownerType = ObjectType.cast(ownerVar.getType()); } if (name != null) { propName = name.substring(ownerName.length() + 1); } } FunctionType overriddenPropType = null; if (ownerType != null && propName != null) { overriddenPropType = findOverriddenFunction(ownerType, propName); } FunctionTypeBuilder builder = new FunctionTypeBuilder(name, compiler, errorRoot, sourceName, scope) .setContents(getFunctionAnalysisResults(fnRoot)) .inferFromOverriddenFunction(overriddenPropType, parametersNode) .inferTemplateTypeName(info) .inferReturnType(info) .inferInheritance(info); // Infer the context type. boolean searchedForThisType = false; if (ownerType != null && ownerType.isFunctionPrototypeType()) { builder.inferThisType( info, ownerType.getOwnerFunction().getInstanceType()); searchedForThisType = true; } else if (ownerNode != null && ownerNode.isThis()) { builder.inferThisType(info, ownerNode.getJSType()); searchedForThisType = true; } if (!searchedForThisType) { builder.inferThisType(info); } functionType = builder .inferParameterTypes(parametersNode, info) .buildAndRegister(); } } // all done return functionType; }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> /** * Find the function that's being overridden on this type, if any. */ private FunctionType findOverriddenFunction( ObjectType ownerType, String propName) { // First, check to see if the property is implemented // on a superclass. JSType propType = ownerType.getPropertyType(propName); if (propType != null && propType.isFunctionType()) { return propType.toMaybeFunctionType(); } else { // If it's not, then check to see if it's implemented // on an implemented interface. for (ObjectType iface : ownerType.getCtorImplementedInterfaces()) { propType = iface.getPropertyType(propName); if (propType != null && propType.isFunctionType()) { return propType.toMaybeFunctionType(); } } } return null; } /** * Creates a new enum type, based on the given nodes. * * This handles two cases that are semantically very different, but * are not mutually exclusive: * - An object literal that needs an enum type attached to it. * - An assignment expression with an enum tag in the jsdoc. * * This function will always create an enum type, so only call it if * you're sure that's what you want. * * @param rValue The node of the enum. * @param name The enum's name * @param info The {@link JSDocInfo} attached to the enum definition. * @param lValueNode The node where this function is being * assigned. */ private EnumType createEnumTypeFromNodes(Node rValue, String name, JSDocInfo info, Node lValueNode) { Preconditions.checkNotNull(info); Preconditions.checkState(info.hasEnumParameterType()); EnumType enumType = null; if (rValue != null && rValue.isQualifiedName()) { // Handle an aliased enum. Var var = scope.getVar(rValue.getQualifiedName()); if (var != null && var.getType() instanceof EnumType) { enumType = (EnumType) var.getType(); } } if (enumType == null) { JSType elementsType = info.getEnumParameterType().evaluate(scope, typeRegistry); enumType = typeRegistry

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>.createEnumType(name, rValue, elementsType); if (rValue != null && rValue.isObjectLit()) { // collect enum elements Node key = rValue.getFirstChild(); while (key != null) { String keyName = NodeUtil.getStringValue(key); if (keyName == null) { // GET and SET don't have a String value; compiler.report( JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); } else if (!codingConvention.isValidEnumKey(keyName)) { compiler.report( JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); } else { enumType.defineElement(keyName, key); } key = key.getNext(); } } } if (name != null && scope.isGlobal()) { typeRegistry.declareType(name, enumType.getElementsType()); } return enumType; } /** * Defines a typed variable. The defining node will be annotated with the * variable's type or {@code null} if its type is inferred. * @param name the defining node. It must be a {@link Token#NAME}. * @param parent the {@code name}'s parent. * @param type the variable's type. It may be {@code null}, in which case * the variable's type will be inferred. */ private void defineSlot(Node name, Node parent, JSType type) { defineSlot(name, parent, type, type == null); } /** * Defines a typed variable. The defining node will be annotated with the * variable's type of {@link JSTypeNative#UNKNOWN_TYPE} if its type is * inferred. * * Slots may be any variable or any qualified name in the global scope. * * @param n the defining NAME or GETPROP node. * @param parent the {@code n}'s parent. * @param type the variable's type. It may be {@code null} if * {@code inferred} is {@code true}. */ void defineSlot(Node n, Node parent, JSType type, boolean inferred) { Preconditions.checkArgument(inferred || type != null); // Only allow declarations

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>inputId); if (scopeToDeclareIn.isDeclared(variableName, false)) { Var oldVar = scopeToDeclareIn.getVar(variableName); newVar = validator.expectUndeclaredVariable( sourceName, input, n, parent, oldVar, variableName, type); } else { if (!inferred) { setDeferredType(n, type); } newVar = scopeToDeclareIn.declare(variableName, n, type, input, inferred); if (type instanceof EnumType) { Node initialValue = newVar.getInitialValue(); boolean isValidValue = initialValue != null && (initialValue.isObjectLit() || initialValue.isQualifiedName()); if (!isValidValue) { compiler.report(JSError.make(sourceName, n, ENUM_INITIALIZER)); } } } // We need to do some additional work for constructors and interfaces. FunctionType fnType = JSType.toMaybeFunctionType(type); if (fnType != null && // We don't want to look at empty function types. !type.isEmptyType()) { if ((fnType.isConstructor() || fnType.isInterface()) && !fnType.equals(getNativeType(U2U_CONSTRUCTOR_TYPE))) { // Declare var.prototype in the scope chain. FunctionType superClassCtor = fnType.getSuperClassConstructor(); ObjectType.Property prototypeSlot = fnType.getSlot("prototype"); // When we declare the function prototype implicitly, we // want to make sure that the function and its prototype // are declared at the same node. We also want to make sure // that the if a symbol has both a Var and a JSType, they have // the same node. // // This consistency is helpful to users of SymbolTable, // because everything gets declared at the same place. prototypeSlot.setNode(n); String prototypeName = variableName + ".prototype"; // There are some rare cases where the prototype will already // be declared. See TypedScopeCreatorTest#testBogusPrototypeInit. // Fortunately, other warnings will complain if this happens. Var prototypeVar = scopeToDeclareIn.getVar(prototypeName); if (prototypeVar != null && prototypeVar.scope == scopeToDeclare

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> the scope for the name of the given node. */ private Scope getQnameRootScope(Node n) { Node root = NodeUtil.getRootOfQualifiedName(n); if (root.isName()) { Var var = scope.getVar(root.getString()); if (var != null) { return var.getScope(); } } return null; } /** * Look for a type declaration on a property assignment * (in an ASSIGN or an object literal key). * * @param info The doc info for this property. * @param lValue The l-value node. * @param rValue The node that {@code n} is being initialized to, * or {@code null} if this is a stub declaration. */ private JSType getDeclaredType(String sourceName, JSDocInfo info, Node lValue, @Nullable Node rValue) { if (info != null && info.hasType()) { return getDeclaredTypeInAnnotation(sourceName, lValue, info); } else if (rValue != null && rValue.isFunction() && shouldUseFunctionLiteralType( JSType.toMaybeFunctionType(rValue.getJSType()), info, lValue)) { return rValue.getJSType(); } else if (info != null) { if (info.hasEnumParameterType()) { if (rValue != null && rValue.isObjectLit()) { return rValue.getJSType(); } else { return createEnumTypeFromNodes( rValue, lValue.getQualifiedName(), info, lValue); } } else if (info.isConstructor() || info.isInterface()) { return createFunctionTypeFromNodes( rValue, lValue.getQualifiedName(), info, lValue); } else { // Check if this is constant, and if it has a known type. if (info.isConstant()) { JSType knownType = null; if (rValue != null) { if (rValue.getJSType() != null && !rValue.getJSType().isUnknownType()) { return rValue.getJSType(); } else if (rValue.isOr()) { // Check for a very specific JS idiom: // var x = x || TYPE;

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> // This is used by Closure's base namespace for esoteric // reasons. Node firstClause = rValue.getFirstChild(); Node secondClause = firstClause.getNext(); boolean namesMatch = firstClause.isName() && lValue.isName() && firstClause.getString().equals(lValue.getString()); if (namesMatch && secondClause.getJSType() != null && !secondClause.getJSType().isUnknownType()) { return secondClause.getJSType(); } } } } } } return getDeclaredTypeInAnnotation(sourceName, lValue, info); } private FunctionType getFunctionType(@Nullable Var v) { JSType t = v == null ? null : v.getType(); ObjectType o = t == null ? null : t.dereference(); return JSType.toMaybeFunctionType(o); } /** * Look for calls that set a delegate method's calling convention. */ private void checkForCallingConventionDefiningCalls( Node n, Map<String, String> delegateCallingConventions) { codingConvention.checkForCallingConventionDefiningCalls(n, delegateCallingConventions); } /** * Look for class-defining calls. * Because JS has no 'native' syntax for defining classes, * this is often very coding-convention dependent and business-logic heavy. */ private void checkForClassDefiningCalls( NodeTraversal t, Node n, Node parent) { SubclassRelationship relationship = codingConvention.getClassesDefinedByCall(n); if (relationship != null) { FunctionType superCtor = getFunctionType( scope.getVar(relationship.superclassName)); FunctionType subCtor = getFunctionType( scope.getVar(relationship.subclassName)); if (superCtor != null && superCtor.isConstructor() && subCtor != null && subCtor.isConstructor()) { ObjectType superClass = superCtor.getInstanceType(); ObjectType subClass = subCtor.getInstanceType(); // superCtor and subCtor might be structural constructors // (like {function(new:Object)}) so we need to resolve them back // to the original ctor objects. superCtor = superClass.getConstructor(); subCtor = subClass.getConstructor(); if (relationship.type == SubclassType.

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> a terrible, terrible hack // to bail out on recusive typedefs. We'll eventually need // to handle these properly. typeRegistry.declareType(typedef, getNativeType(UNKNOWN_TYPE)); JSType realType = info.getTypedefType().evaluate(scope, typeRegistry); if (realType == null) { compiler.report( JSError.make( t.getSourceName(), candidate, MALFORMED_TYPEDEF, typedef)); } typeRegistry.overwriteDeclaredType(typedef, realType); if (candidate.isGetProp()) { defineSlot(candidate, candidate.getParent(), getNativeType(NO_TYPE), false); } } } // end GlobalScopeBuilder /** * A shallow traversal of a local scope to find all arguments and * local variables. */ private final class LocalScopeBuilder extends AbstractScopeBuilder { /** * @param scope The scope that we're builidng. */ private LocalScopeBuilder(Scope scope) { super(scope); } /** * Traverse the scope root and build it. */ void build() { NodeTraversal.traverse(compiler, scope.getRootNode(), this); AstFunctionContents contents = getFunctionAnalysisResults(scope.getRootNode()); if (contents != null) { for (String varName : contents.getEscapedVarNames()) { Var v = scope.getVar(varName); Preconditions.checkState(v.getScope() == scope); v.markEscaped(); } } } /** * Visit a node in a local scope, and add any local variables or catch * parameters into the local symbol table. * * @param t The node traversal. * @param n The node being visited. * @param parent The parent of n */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n == scope.getRootNode()) return; if (n.isParamList() && parent == scope.getRootNode()) { handleFunctionInputs(parent); return; } super.visit(t, n, parent); } /** Handle bleeding functions and function parameters. */ private void handleFunctionInputs(Node fnNode) { // Handle bleeding functions.

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> Node fnNameNode = fnNode.getFirstChild(); String fnName = fnNameNode.getString(); if (!fnName.isEmpty()) { Scope.Var fnVar = scope.getVar(fnName); if (fnVar == null || // Make sure we're not touching a native function. Native // functions aren't bleeding, but may not have a declaration // node. (fnVar.getNameNode() != null && // Make sure that the function is actually bleeding by checking // if has already been declared. fnVar.getInitialValue() != fnNode)) { defineSlot(fnNameNode, fnNode, fnNode.getJSType(), false); } } declareArguments(fnNode); } /** * Declares all of a function's arguments. */ private void declareArguments(Node functionNode) { Node astParameters = functionNode.getFirstChild().getNext(); Node body = astParameters.getNext(); FunctionType functionType = JSType.toMaybeFunctionType(functionNode.getJSType()); if (functionType != null) { Node jsDocParameters = functionType.getParametersNode(); if (jsDocParameters != null) { Node jsDocParameter = jsDocParameters.getFirstChild(); for (Node astParameter : astParameters.children()) { if (jsDocParameter != null) { defineSlot(astParameter, functionNode, jsDocParameter.getJSType(), false); jsDocParameter = jsDocParameter.getNext(); } else { defineSlot(astParameter, functionNode, null, true); } } } } } // end declareArguments } // end LocalScopeBuilder /** * Does a first-order function analysis that just looks at simple things * like what variables are escaped, and whether 'this' is used. */ private static class FirstOrderFunctionAnalyzer extends AbstractScopedCallback implements CompilerPass { private final AbstractCompiler compiler; private final Map<Node, AstFunctionContents> data; FirstOrderFunctionAnalyzer( AbstractCompiler compiler, Map<Node, AstFunctionContents> outParam) { this.compiler = compiler; this.data = outParam; } @Override public void process(Node externs, Node root) { if (externs == null)

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS> { NodeTraversal.traverse(compiler, root, this); } else { NodeTraversal.traverseRoots( compiler, ImmutableList.of(externs, root), this); } } @Override public void enterScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); data.put(n, new AstFunctionContents(n)); } } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (t.inGlobalScope()) { return; } if (n.isReturn() && n.getFirstChild() != null) { data.get(t.getScopeRoot()).recordNonEmptyReturn(); } else if (n.isName() && NodeUtil.isLValue(n)) { String name = n.getString(); Scope scope = t.getScope(); Var var = scope.getVar(name); if (var != null) { Scope ownerScope = var.getScope(); if (scope != ownerScope && ownerScope.isLocal()) { data.get(ownerScope.getRootNode()).recordEscapedVarName(name); } } } } } private AstFunctionContents getFunctionAnalysisResults(@Nullable Node n) { if (n == null) { return null; } // Sometimes this will return null in things like // NameReferenceGraphConstruction that build partial scopes. return functionAnalysisResults.get(n); } }

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>/* * Copyright 2009 The Closure Compiler Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; import com.google.javascript.jscomp.graph.GraphvizGraph; import com.google.javascript.jscomp.graph.LinkedDirectedGraph; import com.google.javascript.rhino.Node; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.Set; /** * Pass factories and meta-data for native Compiler passes. * * @author nicksantos@google.com (Nick Santos) */ public abstract class PassConfig { // Used by subclasses in this package. final CompilerOptions options; /** * A memoized version of scopeCreator. It must be memoized so that * we can make two separate passes over the AST, one for inferring types * and one for checking types. */ private MemoizedScopeCreator typedScopeCreator; /** * This is the scope creator that {@code TypedScopeCreator} delegates to. */ private TypedScopeCreator internalScopeCreator; /** The global typed scope. */ Scope topScope = null; public PassConfig(CompilerOptions options) { this.options = options; } /** * Regenerates the top scope from scratch. * * @param compiler The compiler for which the global scope is regenerated. * @param root The root of the AST. */ void regenerateGlobalTypedScope(AbstractCompiler compiler, Node root) { internalScopeCreator = new TypedScope

Closure, 163

<FILEB>
<CHANGES>
<CHANGEE>
<CHANGES>
Node n = t.getCurrentNode();
if (n.isFunction()) {
String propName = getPrototypePropertyNameFromRValue(n);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY),
t.getScope()));
} else if (isGlobalFunctionDeclaration(t, n)) {
Node parent = n.getParent();
String name = parent.isName()?
parent.getString() /* VAR */ :
n.getFirstChild().getString() /* named function */;
symbolStack.push(
new NameContext(getNameInfoForName(name, VAR), t.getScope()));
} else {
<CHANGEE>
<CHANGES>
symbolStack.push(new NameContext(anonymousNode, t.getScope()));
}
} else {
Preconditions.checkState(t.inGlobalScope());
symbolStack.push(new NameContext(globalNode, t.getScope()));
}
<CHANGEE>
<CHANGES>
symbolStack.pop();
<CHANGEE>
<CHANGES>
String propName = processNonFunctionPrototypeAssign(n, parent);
if (propName!= null) {
symbolStack.push(
new NameContext(
getNameInfoForName(propName, PROPERTY), null));
<CHANGEE>
<CHANGES>
if (n.isQualifiedName()) {
<CHANGEE>
<CHANGES>
if (processPrototypeRef(t, n)) {
return;
}
<CHANGEE>
<CHANGES>
return;
<CHANGEE>
<CHANGES>
if (n.getParent().isAssign() && n.getNext()!= null) {
String rValueName = getPrototypePropertyNameFromRValue(n);
if (rValueName!= null) {
return;
}
}
}
<CHANGEE>
<CHANGES>
addSymbolUse(propName, t.getModule(), PROPERTY);
} else if (n.isObjectLit()) {
<CHANGEE>
<CHANGES>
String lValueName = NodeUtil.getBestLValueName(
NodeUtil.getBestLValue(n));
if (lValueName!= null && lValueName.endsWith(".prototype")) {
return;
}
<CHANGEE>
<CHANGES>
if (!processGlobalFunctionDeclaration(t, n, var)) {
<CHANGEE>
<CHANGES>
if (processNonFunctionPrototypeAssign(n, parent)!= null) {
<CHANGEE>
<CHANGES>
private String processNonFunctionPrototypeAssign(Node n, Node parent) {
if (isAssignRValue(n, parent) &&!n.isFunction()) {
return getPrototypePropertyNameFromRValue(n);
}
return null;
}
<CHANGEE>
<CHANGES>
Scope s = t.getScope();
if (!(s.isGlobal() ||
s.getDepth() == 1 && s.getRootNode() == n)) {
return false;
}
return NodeUtil.isFunctionDeclaration(n) ||
n.isFunction() && n.getParent().isName();
<CHANGEE>
<CHANGES>
private boolean isAssignRValue(Node n, Node parent) {
return parent!= null && parent.isAssign() && parent.getFirstChild()!= n;
}
<CHANGEE>
<CHANGES>
private String getPrototypePropertyNameFromRValue(Node rValue) {
Node lValue = NodeUtil.getBestLValue(rValue);
if (lValue == null ||
lValue.getParent() == null ||
lValue.getParent().getParent() == null ||
!(NodeUtil.isObjectLitKey(lValue, lValue.getParent()) ||
NodeUtil.isExprAssign(lValue.getParent().getParent()))) {
return null;
<CHANGEE>
<CHANGES>
String lValueName =
NodeUtil.getBestLValueName(NodeUtil.getBestLValue(rValue));
if (lValueName == null) {
return null;
}
int lastDot = lValueName.lastIndexOf('.');
if (lastDot == -1) {
return null;
}
String firstPart = lValueName.substring(0, lastDot);
if (!firstPart.endsWith(".prototype")) {
return null;
}
return lValueName.substring(lastDot + 1);
<CHANGEE>
<CHANGES>
Node nameNode, Var v) {
<CHANGEE>
<CHANGES>
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
new GlobalFunction(nameNode, v, t.getModule()));
<CHANGEE>
<CHANGES>
private boolean processPrototypeRef(NodeTraversal t, Node ref) {
Node root = NodeUtil.getRootOfQualifiedName(ref);
Node n = ref.getParent();
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
t.getScope().getVar(root.getString()),
<CHANGEE>
<CHANGES>
return true;
<CHANGEE>
<CHANGES>
return false;
<CHANGEE>
<CHANGES>
Var getRootVar();
<CHANGEE>
<CHANGES>
private final Var var;
<CHANGEE>
<CHANGES>
GlobalFunction(Node nameNode, Var var, JSModule module) {
Node parent = nameNode.getParent();
<CHANGEE>
<CHANGES>
this.var = var;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return var;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
AssignmentProperty(Node node, Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
private final Var rootVar;
<CHANGEE>
<CHANGES>
Var rootVar, JSModule module) {
<CHANGEE>
<CHANGES>
this.rootVar = rootVar;
<CHANGEE>
<CHANGES>
public Var getRootVar() {
return rootVar;
}
@Override
<CHANGEE>
<CHANGES>
final Scope scope;
NameContext(NameInfo name, Scope scope) {
<CHANGEE>
<CHANGES>
this.scope = scope;
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(prop.getRootVar()!= null && prop.getRootVar().isGlobal())) {
continue;
}
<CHANGEE>
<FILEE>
<FILEB> } else { NameInfo nameInfo = new NameInfo(name); map.put(name, nameInfo); symbolGraph.createNode(nameInfo); return nameInfo; } } private class ProcessProperties implements NodeTraversal.ScopedCallback { // There are two types of context information on this stack: // 1) Every scope has a NameContext corresponding to its scope. // Variables are given VAR contexts. // Prototype properties are given PROPERTY contexts. // The global scope is given the special [global] context. // And function expressions that we aren't able to give a reasonable // name are given a special [anonymous] context. // 2) Every assignment of a prototype property of a non-function is // given a name context. These contexts do not have scopes. private Stack<NameContext> symbolStack = new Stack<NameContext>(); <CHANGES> private ProcessProperties() { symbolStack.push(new NameContext(globalNode)); } <CHANGEE> @Override public void enterScope(NodeTraversal t) { <CHANGES> symbolStack.peek().scope = t.getScope(); <CHANGEE> // NOTE(nicksantos): We use the same anonymous node for all // functions that do not have reasonable names. I can't remember // at the moment why we do this. I think it's because anonymous // nodes can never have in-edges. They're just there as a placeholder // for scope information, and do not matter in the edge propagation. <CHANGES> <CHANGEE> } @Override public void exitScope(NodeTraversal t) { <CHANGES> <CHANGEE> } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { // Process prototype assignments to non-functions. <CHANGES> if (isPrototypePropertyAssign(n)) { symbolStack.push(new NameContext(getNameInfoForName( n.getFirstChild().getLastChild().getString(), PROPERTY))); } else if (isGlobalFunctionDeclaration(t, n)) { String name = parent.isName() ? parent.getString() /* VAR */ : n.getFirstChild().getString() /* named function */; symbolStack.push(new NameContext(getNameInfoForName(name, VAR))); } else if (n.isFunction()) { symbolStack.push(new<SCANS>Creator(compiler); typedScopeCreator = new MemoizedScopeCreator(internalScopeCreator); topScope = typedScopeCreator.createScope(root, null); } /** * Regenerates the top scope potentially only for a sub-tree of AST and then * copies information for the old global scope. * * @param compiler The compiler for which the global scope is generated. * @param scriptRoot The root of the AST used to generate global scope. */ void patchGlobalTypedScope(AbstractCompiler compiler, Node scriptRoot) { Preconditions.checkNotNull(internalScopeCreator); internalScopeCreator.patchGlobalScope(topScope, scriptRoot); } /** * Gets the scope creator for typed scopes. */ MemoizedScopeCreator getTypedScopeCreator() { return typedScopeCreator; } /** * Gets the global scope, with type information. */ Scope getTopScope() { return topScope; } /** * Gets the checking passes to run. * * Checking passes revolve around emitting warnings and errors. * They also may include pre-processor passes needed to do * error analysis more effectively. * * Clients that only want to analyze code (like IDEs) and not emit * code will only run checks and not optimizations. */ abstract protected List<PassFactory> getChecks(); /** * Gets the optimization passes to run. * * Optimization passes revolve around producing smaller and faster code. * They should always run after checking passes. */ abstract protected List<PassFactory> getOptimizations(); /** * Gets a graph of the passes run. For debugging. */ GraphvizGraph getPassGraph() { LinkedDirectedGraph<String, String> graph = LinkedDirectedGraph.createWithoutAnnotations(); Iterable<PassFactory> allPasses = Iterables.concat(getChecks(), getOptimizations()); String lastPass = null; String loopStart = null; for (PassFactory pass : allPasses) { String passName = pass.getName(); int i = 1; while (graph.hasNode(passName)) { passName = pass.getName() + (i++); } graph.createNode(passName); if (loopStart == null && !pass.isOneTime